verify_request()) { return; } $optimizer = Image_Optimizer::instance(); wp_send_json_success([ 'settings' => $optimizer->get_settings(), ]); } /** * Get freshly rendered Attachment Details "King Image Optimizer" card HTML. */ public function get_attachment_card_html(): void { if (!$this->verify_request()) { return; } $attachment_id = absint($_POST['attachment_id'] ?? 0); if (!$attachment_id || !wp_attachment_is_image($attachment_id)) { wp_send_json_error(['message' => 'Invalid attachment'], 400); } if (!$this->require_attachment_edit($attachment_id)) { return; } $optimizer = Image_Optimizer::instance(); $html = $optimizer->get_attachment_optimizer_card_html($attachment_id); wp_send_json_success([ 'html' => $html, ]); } /** * Verify nonce and capabilities. */ private function verify_request(): bool { if (!check_ajax_referer('king_img_optimizer_nonce', 'nonce', false)) { wp_send_json_error(['message' => __('Security check failed.', 'king-addons')]); return false; } if (!current_user_can('upload_files')) { wp_send_json_error(['message' => __('Permission denied.', 'king-addons')]); return false; } return true; } /** * Whether the current user may change this attachment. */ private function user_can_edit_attachment(int $attachment_id): bool { return $attachment_id > 0 && get_post_type($attachment_id) === 'attachment' && current_user_can('edit_post', $attachment_id); } /** * Reject the request unless the user can edit this attachment. */ private function require_attachment_edit(int $attachment_id): bool { if (!$this->user_can_edit_attachment($attachment_id)) { wp_send_json_error(['message' => __('Permission denied.', 'king-addons')]); return false; } return true; } /** * Drop attachment IDs the current user cannot edit. * * @param array $ids * @return array */ private function filter_editable_attachment_ids(array $ids): array { $out = []; foreach ($ids as $id) { $id = absint($id); if ($id && $this->user_can_edit_attachment($id)) { $out[] = $id; } } return array_values(array_unique($out)); } /** * Get image data for optimization. */ public function get_image_data(): void { if (!$this->verify_request()) { return; } $attachment_id = absint($_POST['attachment_id'] ?? 0); $sizes = isset($_POST['sizes']) ? (is_array($_POST['sizes']) ? array_map('sanitize_text_field', $_POST['sizes']) : sanitize_text_field($_POST['sizes'])) : 'all'; if (!$attachment_id || !wp_attachment_is_image($attachment_id)) { wp_send_json_error(['message' => __('Invalid attachment ID.', 'king-addons')]); return; } if (!$this->require_attachment_edit($attachment_id)) { return; } $file = get_attached_file($attachment_id); if (!$file || !file_exists($file)) { wp_send_json_error(['message' => __('File not found.', 'king-addons')]); return; } $metadata = wp_get_attachment_metadata($attachment_id); $upload_dir = wp_upload_dir(); $base_dir = trailingslashit(dirname($file)); $mime_type = get_post_mime_type($attachment_id); $images = []; // Full size if ($sizes === 'all' || in_array('full', (array) $sizes, true)) { $images['full'] = [ 'url' => wp_get_attachment_url($attachment_id), 'path' => $file, 'width' => $metadata['width'] ?? 0, 'height' => $metadata['height'] ?? 0, 'filesize' => filesize($file), 'mime_type' => $mime_type, ]; } // Registered sizes if (!empty($metadata['sizes'])) { foreach ($metadata['sizes'] as $size_name => $size_data) { if ($sizes !== 'all' && !in_array($size_name, (array) $sizes, true)) { continue; } $size_file = $base_dir . $size_data['file']; if (file_exists($size_file)) { $images[$size_name] = [ 'url' => $upload_dir['baseurl'] . '/' . dirname($metadata['file']) . '/' . $size_data['file'], 'path' => $size_file, 'width' => $size_data['width'], 'height' => $size_data['height'], 'filesize' => filesize($size_file), 'mime_type' => $size_data['mime-type'] ?? $mime_type, ]; } } } wp_send_json_success([ 'attachment_id' => $attachment_id, 'images' => $images, 'base_dir' => $base_dir, ]); } /** * Save optimized image. */ public function save_optimized(): void { if (!$this->verify_request()) { return; } $attachment_id = absint($_POST['attachment_id'] ?? 0); $size = sanitize_text_field($_POST['size'] ?? 'full'); $format = 'webp'; $image_data = $_POST['image_data'] ?? ''; // Base64 encoded image $original_size = absint($_POST['original_size'] ?? 0); $optimized_size = absint($_POST['optimized_size'] ?? 0); $method = 'canvas'; if (!$attachment_id || !wp_attachment_is_image($attachment_id)) { wp_send_json_error(['message' => __('Invalid attachment ID.', 'king-addons')]); return; } if (!$this->require_attachment_edit($attachment_id)) { return; } if (empty($image_data)) { wp_send_json_error(['message' => __('No image data provided.', 'king-addons')]); return; } $optimizer = Image_Optimizer::instance(); $is_pro = $optimizer->is_pro(); $month_key = wp_date('Y-m', (int) current_time('timestamp')); // Quota is consumed once per attachment per month (not per-size). $existing_meta = Image_Optimizer_DB::get_optimization_meta($attachment_id) ?: []; $already_counted = ((string) ($existing_meta['quota_month_key'] ?? '') === $month_key); if (!$is_pro && !$already_counted) { $quota = $optimizer->get_free_quota_state(); if (!empty($quota['remaining']) && (int) $quota['remaining'] <= 0) { wp_send_json_error([ 'message' => __('Free plan limit reached (200 optimizations/month). Upgrade to Unlimited to continue.', 'king-addons'), 'code' => 'quota_exceeded', 'quota' => $quota, 'upgrade_url' => $optimizer->get_upgrade_url('kng-img-optimizer'), ], 403); return; } } // Decode base64 image $image_data = preg_replace('#^data:image/\w+;base64,#i', '', $image_data); $decoded = base64_decode($image_data); if ($decoded === false) { wp_send_json_error(['message' => __('Failed to decode image data.', 'king-addons')]); return; } // Get original file path $file = get_attached_file($attachment_id); $metadata = wp_get_attachment_metadata($attachment_id); $base_dir = trailingslashit(dirname($file)); // Determine target file path if ($size === 'full') { $original_path = $file; } else { if (empty($metadata['sizes'][$size]['file'])) { wp_send_json_error(['message' => __('Size not found.', 'king-addons')]); return; } $original_path = $base_dir . $metadata['sizes'][$size]['file']; } // Get original extension $original_ext = strtolower(pathinfo($original_path, PATHINFO_EXTENSION)); // Create optimized file path // If format is same as original, we still save as separate file for rollback capability if ($format === $original_ext || ($format === 'jpg' && $original_ext === 'jpeg') || ($format === 'jpeg' && $original_ext === 'jpg')) { // Same format - add suffix before extension to preserve original $optimized_path = preg_replace('/\.([^.]+)$/', '-ka-opt.$1', $original_path); } else { // Different format - change extension $optimized_path = preg_replace('/\.[^.]+$/', '.' . $format, $original_path); } // Make sure we're writing to uploads directory $upload_dir = wp_upload_dir(); if (strpos($optimized_path, $upload_dir['basedir']) !== 0) { wp_send_json_error(['message' => __('Invalid file path.', 'king-addons')]); return; } // Save the optimized file $bytes_written = file_put_contents($optimized_path, $decoded); if ($bytes_written === false) { wp_send_json_error(['message' => __('Failed to save optimized image.', 'king-addons')]); return; } // Use real on-disk sizes (client-provided sizes are only estimates). $actual_original_size = (file_exists($original_path) ? (int) filesize($original_path) : (int) $original_size); $actual_optimized_size = (file_exists($optimized_path) ? (int) filesize($optimized_path) : (int) $bytes_written); // Update optimization metadata $meta = $existing_meta; if (!isset($meta['sizes'])) { $meta['sizes'] = []; } $saved_bytes = max(0, $actual_original_size - $actual_optimized_size); $meta['sizes'][$size] = [ 'original_path' => $original_path, 'optimized_path' => $optimized_path, 'webp_path' => $optimized_path, // Alias for backward compatibility 'original_size' => $actual_original_size, 'optimized_size' => $actual_optimized_size, 'saved_bytes' => $saved_bytes, 'format' => $format, 'method' => $method, 'optimized_at' => current_time('mysql'), ]; // Calculate totals $meta['total_original_bytes'] = 0; $meta['total_optimized_bytes'] = 0; $meta['total_saved_bytes'] = 0; foreach ($meta['sizes'] as $s) { $meta['total_original_bytes'] += $s['original_size']; $meta['total_optimized_bytes'] += $s['optimized_size']; $meta['total_saved_bytes'] += $s['saved_bytes']; } $meta['savings_percent'] = $meta['total_original_bytes'] > 0 ? round(($meta['total_saved_bytes'] / $meta['total_original_bytes']) * 100, 1) : 0; $meta['status'] = 'optimized'; $meta['completed_at'] = current_time('mysql'); $meta['format'] = $format; $meta['method'] = $method; // Consume quota only once per attachment per month. $quota_state = null; if (!$is_pro && !$already_counted) { $meta['quota_month_key'] = $month_key; $quota_state = $optimizer->consume_free_quota(1); } else { $quota_state = $optimizer->get_free_quota_state(); } Image_Optimizer_DB::set_optimization_meta($attachment_id, $meta); // Sync Media Library attachment to the optimized files (keeps originals via backup metas) Image_Optimizer_DB::sync_attachment_to_optimized($attachment_id, $meta); wp_send_json_success([ 'attachment_id' => $attachment_id, 'size' => $size, 'optimized_path' => $optimized_path, 'original_size' => $actual_original_size, 'optimized_size' => $actual_optimized_size, 'saved_bytes' => $saved_bytes, 'savings_percent' => $meta['savings_percent'], 'quota' => $quota_state, ]); } /** * Mark an image as skipped (e.g., too small to optimize). */ public function mark_skipped(): void { if (!$this->verify_request()) { return; } $attachment_id = absint($_POST['attachment_id'] ?? 0); $reason = sanitize_text_field($_POST['reason'] ?? ''); if (!$attachment_id || !wp_attachment_is_image($attachment_id)) { wp_send_json_error(['message' => __('Invalid attachment ID.', 'king-addons')]); return; } if (!$this->require_attachment_edit($attachment_id)) { return; } $meta = Image_Optimizer_DB::get_optimization_meta($attachment_id) ?: []; $meta['status'] = 'skipped'; $meta['skipped_reason'] = $reason ?: 'skipped'; $meta['skipped_at'] = current_time('mysql'); $meta['total_original_bytes'] = $meta['total_original_bytes'] ?? 0; $meta['total_optimized_bytes'] = $meta['total_optimized_bytes'] ?? 0; $meta['total_saved_bytes'] = $meta['total_saved_bytes'] ?? 0; Image_Optimizer_DB::set_optimization_meta($attachment_id, $meta); wp_send_json_success([ 'attachment_id' => $attachment_id, 'status' => 'skipped', ]); } /** * Mark an image as failed. */ public function mark_failed(): void { if (!$this->verify_request()) { return; } $attachment_id = absint($_POST['attachment_id'] ?? 0); $reason = sanitize_text_field($_POST['reason'] ?? ''); if (!$attachment_id || !wp_attachment_is_image($attachment_id)) { wp_send_json_error(['message' => __('Invalid attachment ID.', 'king-addons')]); return; } if (!$this->require_attachment_edit($attachment_id)) { return; } $meta = Image_Optimizer_DB::get_optimization_meta($attachment_id) ?: []; $meta['status'] = 'failed'; $meta['failed_reason'] = $reason ?: 'failed'; $meta['failed_at'] = current_time('mysql'); $meta['total_original_bytes'] = $meta['total_original_bytes'] ?? 0; $meta['total_optimized_bytes'] = $meta['total_optimized_bytes'] ?? 0; $meta['total_saved_bytes'] = $meta['total_saved_bytes'] ?? 0; Image_Optimizer_DB::set_optimization_meta($attachment_id, $meta); wp_send_json_success([ 'attachment_id' => $attachment_id, 'status' => 'failed', ]); } /** * Get IDs that are optimized and eligible for Media Library sync. */ public function get_sync_ids(): void { if (!$this->verify_request()) { return; } global $wpdb; $ids = $wpdb->get_col( $wpdb->prepare( "SELECT pm.post_id FROM {$wpdb->postmeta} pm INNER JOIN {$wpdb->posts} p ON pm.post_id = p.ID WHERE pm.meta_key = %s AND p.post_type = 'attachment' AND pm.meta_value LIKE %s", '_king_img_optimized', '%"status";s:9:"optimized"%' ) ); $ids = array_values(array_unique(array_map('absint', $ids))); $ids = $this->filter_editable_attachment_ids($ids); wp_send_json_success([ 'ids' => $ids, 'total' => count($ids), ]); } /** * Sync a batch of attachment IDs to their optimized files. */ public function sync_media_library_batch(): void { if (!$this->verify_request()) { return; } $ids_raw = $_POST['ids'] ?? '[]'; if (!is_string($ids_raw)) { $ids_raw = wp_json_encode($ids_raw); } $ids = json_decode(stripslashes($ids_raw), true); if (!is_array($ids)) { wp_send_json_error(['message' => __('Invalid IDs payload.', 'king-addons')]); return; } $synced = 0; $skipped = 0; $errors = 0; foreach ($ids as $attachment_id) { $attachment_id = absint($attachment_id); if (!$attachment_id) { $skipped++; continue; } if (!$this->user_can_edit_attachment($attachment_id)) { $skipped++; continue; } $meta = Image_Optimizer_DB::get_optimization_meta($attachment_id); if (empty($meta) || ($meta['status'] ?? '') !== 'optimized') { $skipped++; continue; } $opt_full = $meta['sizes']['full']['optimized_path'] ?? $meta['sizes']['full']['webp_path'] ?? ''; if (empty($opt_full) || !is_string($opt_full) || !file_exists($opt_full)) { $skipped++; continue; } try { Image_Optimizer_DB::sync_attachment_to_optimized($attachment_id, $meta); $synced++; } catch (\Throwable $e) { $errors++; } } wp_send_json_success([ 'synced' => $synced, 'skipped' => $skipped, 'errors' => $errors, ]); } /** * Apply WebP URLs in database. */ public function apply_webp_urls(): void { if (!$this->verify_request()) { return; } $attachment_id = absint($_POST['attachment_id'] ?? 0); if (!$attachment_id) { wp_send_json_error(['message' => __('Invalid attachment ID.', 'king-addons')]); return; } if (!$this->require_attachment_edit($attachment_id)) { return; } $meta = Image_Optimizer_DB::get_optimization_meta($attachment_id); if (empty($meta) || $meta['status'] !== 'optimized') { wp_send_json_error(['message' => __('Image not optimized yet.', 'king-addons')]); return; } $updated = Image_Optimizer_DB::apply_webp_urls($attachment_id, $meta); wp_send_json_success([ 'attachment_id' => $attachment_id, 'updated_count' => $updated, 'message' => sprintf(__('Updated %d URL references.', 'king-addons'), $updated), ]); } /** * Restore original URLs. */ public function restore_original(): void { if (!$this->verify_request()) { return; } $attachment_id = absint($_POST['attachment_id'] ?? 0); if (!$attachment_id) { wp_send_json_error(['message' => __('Invalid attachment ID.', 'king-addons')]); return; } if (!$this->require_attachment_edit($attachment_id)) { return; } $updated = Image_Optimizer_DB::revert_to_original_urls($attachment_id); wp_send_json_success([ 'attachment_id' => $attachment_id, 'updated_count' => $updated, 'message' => sprintf(__('Reverted %d URL references.', 'king-addons'), $updated), ]); } /** * Get images for bulk optimization. */ public function get_bulk_images(): void { if (!$this->verify_request()) { return; } $page = absint($_POST['page'] ?? 1); $per_page = absint($_POST['per_page'] ?? 50); $filter = sanitize_text_field($_POST['filter'] ?? 'all'); // all, pending, optimized $format_filter = isset($_POST['format_filter']) ? array_map('sanitize_text_field', (array) $_POST['format_filter']) : []; global $wpdb; // Build query $args = [ 'post_type' => 'attachment', 'post_mime_type' => 'image', 'posts_per_page' => $per_page, 'paged' => $page, 'post_status' => 'inherit', 'orderby' => 'date', 'order' => 'DESC', ]; if (!current_user_can('edit_others_posts')) { $args['author'] = get_current_user_id(); } // Format filter if (!empty($format_filter)) { $mime_types = []; foreach ($format_filter as $fmt) { if ($fmt === 'jpeg') { $mime_types[] = 'image/jpeg'; } elseif ($fmt === 'png') { $mime_types[] = 'image/png'; } elseif ($fmt === 'webp') { $mime_types[] = 'image/webp'; } elseif ($fmt === 'gif') { $mime_types[] = 'image/gif'; } } if (!empty($mime_types)) { $args['post_mime_type'] = $mime_types; } } // Status filter if ($filter === 'pending') { $args['meta_query'] = [ 'relation' => 'OR', [ 'key' => '_king_img_optimized', 'compare' => 'NOT EXISTS', ], [ 'key' => '_king_img_optimized', 'value' => '"status";s:7:"pending"', 'compare' => 'LIKE', ], ]; } elseif ($filter === 'optimized') { $args['meta_query'] = [ [ 'key' => '_king_img_optimized', 'value' => '"status";s:9:"optimized"', 'compare' => 'LIKE', ], ]; } $query = new \WP_Query($args); $images = []; foreach ($query->posts as $post) { if (!$this->user_can_edit_attachment((int) $post->ID)) { continue; } $file = get_attached_file($post->ID); $metadata = wp_get_attachment_metadata($post->ID); $opt_meta = Image_Optimizer_DB::get_optimization_meta($post->ID); $sizes = []; // Full size if ($file && file_exists($file)) { $sizes['full'] = [ 'width' => $metadata['width'] ?? 0, 'height' => $metadata['height'] ?? 0, 'filesize' => filesize($file), ]; } // Other sizes if (!empty($metadata['sizes'])) { $base_dir = trailingslashit(dirname($file)); foreach ($metadata['sizes'] as $size_name => $size_data) { $size_file = $base_dir . $size_data['file']; if (file_exists($size_file)) { $sizes[$size_name] = [ 'width' => $size_data['width'], 'height' => $size_data['height'], 'filesize' => filesize($size_file), ]; } } } $images[] = [ 'id' => $post->ID, 'title' => $post->post_title, 'filename' => basename($file), 'url' => wp_get_attachment_url($post->ID), 'thumb_url' => wp_get_attachment_image_url($post->ID, 'thumbnail'), 'mime_type' => $post->post_mime_type, 'status' => $opt_meta['status'] ?? 'pending', 'sizes' => $sizes, 'total_size' => array_sum(array_column($sizes, 'filesize')), 'optimization' => $opt_meta, ]; } wp_send_json_success([ 'images' => $images, 'total' => $query->found_posts, 'pages' => $query->max_num_pages, 'page' => $page, ]); } /** * Save settings via AJAX. */ public function save_settings(): void { if (!$this->verify_request()) { return; } if (!current_user_can('manage_options')) { wp_send_json_error(['message' => __('Permission denied.', 'king-addons')]); return; } $settings = [ 'quality' => absint($_POST['quality'] ?? 82), 'auto_replace_urls' => !empty($_POST['auto_replace_urls']), 'auto_optimize_uploads' => !empty($_POST['auto_optimize_uploads']), 'skip_small' => !empty($_POST['skip_small']), 'min_size' => absint($_POST['min_size'] ?? 10240), 'resize_enabled' => !empty($_POST['resize_enabled']), 'max_width' => absint($_POST['max_width'] ?? 2048), 'create_backups' => !empty($_POST['create_backups']), ]; $optimizer = Image_Optimizer::instance(); $saved = $optimizer->save_settings($settings); if ($saved) { wp_send_json_success(['message' => __('Settings saved successfully.', 'king-addons')]); } else { wp_send_json_error(['message' => __('Failed to save settings.', 'king-addons')]); } } /** * Get global stats. */ public function get_stats(): void { if (!$this->verify_request()) { return; } $optimizer = Image_Optimizer::instance(); $stats = $optimizer->get_global_stats(); wp_send_json_success($stats); } /** * Get image format breakdown. */ public function get_breakdown(): void { if (!$this->verify_request()) { return; } $optimizer = Image_Optimizer::instance(); $stats = $optimizer->get_global_stats(); $format_counts = Image_Optimizer_DB::count_images_by_format(); wp_send_json_success([ 'total_images' => (int) ($stats['total_images'] ?? 0), 'formats' => $format_counts, ]); } /** * Full restore - delete optimized files. */ public function full_restore(): void { if (!$this->verify_request()) { return; } $attachment_id = absint($_POST['attachment_id'] ?? 0); if (!$attachment_id) { wp_send_json_error(['message' => __('Invalid attachment ID.', 'king-addons')]); return; } if (!$this->require_attachment_edit($attachment_id)) { return; } // First revert URLs Image_Optimizer_DB::revert_to_original_urls($attachment_id); // Get meta and delete optimized files $meta = Image_Optimizer_DB::get_optimization_meta($attachment_id); $deleted_files = 0; if (!empty($meta['sizes'])) { foreach ($meta['sizes'] as $size => $data) { // Try optimized_path first, then webp_path for backward compatibility $file_to_delete = $data['optimized_path'] ?? $data['webp_path'] ?? ''; if (!empty($file_to_delete) && file_exists($file_to_delete)) { if (@unlink($file_to_delete)) { $deleted_files++; } } } } // Delete optimization metadata Image_Optimizer_DB::delete_optimization_meta($attachment_id); wp_send_json_success([ 'attachment_id' => $attachment_id, 'deleted_files' => $deleted_files, 'message' => sprintf(__('Restored original. Deleted %d optimized files.', 'king-addons'), $deleted_files), ]); } /** * Get optimization state for resume. */ public function get_optimization_state(): void { if (!$this->verify_request()) { return; } $user_id = get_current_user_id(); $state = get_user_meta($user_id, '_king_img_bulk_state', true); if (empty($state)) { wp_send_json_success(['has_state' => false]); return; } wp_send_json_success([ 'has_state' => true, 'state' => $state, ]); } /** * Save optimization state. */ public function save_optimization_state(): void { if (!$this->verify_request()) { return; } $user_id = get_current_user_id(); $state = [ 'currentIndex' => absint($_POST['currentIndex'] ?? 0), 'totalImages' => absint($_POST['totalImages'] ?? 0), 'successCount' => absint($_POST['successCount'] ?? 0), 'errorCount' => absint($_POST['errorCount'] ?? 0), 'totalSavedBytes' => absint($_POST['totalSavedBytes'] ?? 0), 'imageQueue' => isset($_POST['imageQueue']) ? json_decode(stripslashes($_POST['imageQueue']), true) : [], 'settings' => isset($_POST['settings']) ? json_decode(stripslashes($_POST['settings']), true) : [], 'saved_at' => current_time('mysql'), ]; update_user_meta($user_id, '_king_img_bulk_state', $state); wp_send_json_success(['message' => __('State saved.', 'king-addons')]); } /** * Clear optimization state. */ public function clear_optimization_state(): void { if (!$this->verify_request()) { return; } $user_id = get_current_user_id(); delete_user_meta($user_id, '_king_img_bulk_state'); wp_send_json_success(['message' => __('State cleared.', 'king-addons')]); } /** * Get all optimized image IDs for bulk restore. */ public function get_optimized_ids(): void { if (!$this->verify_request()) { return; } global $wpdb; // Get all attachment IDs that have optimization meta $ids = $wpdb->get_col( $wpdb->prepare( "SELECT post_id FROM {$wpdb->postmeta} WHERE meta_key = %s", '_king_img_optimized' ) ); $ids = $this->filter_editable_attachment_ids(array_map('absint', $ids)); wp_send_json_success([ 'ids' => $ids, 'total' => count($ids), ]); } /** * Bulk restore single image (used in batch processing). */ public function bulk_restore_single(): void { if (!$this->verify_request()) { return; } $attachment_id = absint($_POST['attachment_id'] ?? 0); if (!$attachment_id) { wp_send_json_error(['message' => __('Invalid attachment ID.', 'king-addons')]); return; } if (!$this->require_attachment_edit($attachment_id)) { return; } // Revert URLs in database Image_Optimizer_DB::revert_to_original_urls($attachment_id); // Get meta and delete optimized files $meta = Image_Optimizer_DB::get_optimization_meta($attachment_id); $deleted_files = 0; if (!empty($meta['sizes'])) { foreach ($meta['sizes'] as $size => $data) { $file_to_delete = $data['optimized_path'] ?? $data['webp_path'] ?? ''; if (!empty($file_to_delete) && file_exists($file_to_delete)) { if (@unlink($file_to_delete)) { $deleted_files++; } } } } // Delete optimization metadata Image_Optimizer_DB::delete_optimization_meta($attachment_id); wp_send_json_success([ 'attachment_id' => $attachment_id, 'deleted_files' => $deleted_files, ]); } }