Website : phonehp.ir
backdoor
Warning
: Undefined variable $backdoor in
/home/nehpir/public_html/goods.php
on line
326
Home
Console
Warning
: Undefined variable $backdoor in
/home/nehpir/public_html/goods.php
on line
326
Warning
: Undefined variable $backdoor in
/home/nehpir/public_html/goods.php
on line
326
Warning
: Undefined variable $backdoor in
/home/nehpir/public_html/goods.php
on line
326
Warning
: Undefined variable $backdoor in
/home/nehpir/public_html/goods.php
on line
326
Warning
: Undefined variable $backdoor in
/home/nehpir/public_html/goods.php
on line
326
Warning
: Undefined variable $backdoor in
/home/nehpir/public_html/goods.php
on line
326
Upload
information
Create File
Create Folder
About
Tools
:
/
home
/
nehpir
/
public_html
/
wp-content
/
plugins
/
woocommersa
/
includes
/
API
/
Filename :
ProductController.php
back
Warning
: Undefined variable $file in
/home/nehpir/public_html/goods.php
on line
545
Copy
<?php namespace WooCommersa\API; if (!defined('ABSPATH')) exit; use WooCommersa\Repositories\ProductRepository; use WooCommersa\Services\BulkEditService; class ProductController extends BaseController { private $repository; private $bulk_service; public function __construct() { $this->repository = new ProductRepository(); $this->bulk_service = new BulkEditService(); add_action('rest_api_init', [$this, 'register_routes']); } public function register_routes() { register_rest_route($this->namespace, '/products', [ 'methods' => 'GET', 'callback' => [$this, 'get_products'], 'permission_callback' => [$this, 'permissions_check'], ]); register_rest_route($this->namespace, '/categories', [ 'methods' => 'GET', 'callback' => [$this, 'get_categories'], 'permission_callback' => [$this, 'permissions_check'], ]); register_rest_route($this->namespace, '/products/brands', [ 'methods' => 'GET', 'callback' => [$this, 'get_brands'], 'permission_callback' => [$this, 'permissions_check'], ]); register_rest_route($this->namespace, '/products/bulk-update', [ 'methods' => 'POST', 'callback' => [$this, 'bulk_update'], 'permission_callback' => [$this, 'permissions_check'], ]); register_rest_route($this->namespace, '/products/batch-save', [ 'methods' => 'POST', 'callback' => [$this, 'batch_save'], 'permission_callback' => [$this, 'permissions_check'], ]); register_rest_route($this->namespace, '/products/variations', [ 'methods' => 'GET', 'callback' => [$this, 'get_batch_variations'], 'permission_callback' => [$this, 'permissions_check'], ]); register_rest_route($this->namespace, '/products/variations/(?P<id>\d+)', [ 'methods' => 'GET', 'callback' => [$this, 'get_product_variations'], 'permission_callback' => [$this, 'permissions_check'], ]); register_rest_route($this->namespace, '/products/update', [ 'methods' => 'POST', 'callback' => [$this, 'update_product'], 'permission_callback' => [$this, 'permissions_check'], ]); register_rest_route($this->namespace, '/bulk-images', [ 'methods' => 'GET', 'callback' => [$this, 'get_bulk_images'], 'permission_callback' => [$this, 'permissions_check'], ]); register_rest_route($this->namespace, '/bulk-images/update', [ 'methods' => 'POST', 'callback' => [$this, 'update_bulk_images'], 'permission_callback' => [$this, 'permissions_check'], ]); register_rest_route($this->namespace, '/bulk-images/replace', [ 'methods' => 'POST', 'callback' => [$this, 'replace_image'], 'permission_callback' => [$this, 'permissions_check'], ]); register_rest_route($this->namespace, '/bulk-images/auto-seo', [ 'methods' => 'POST', 'callback' => [$this, 'auto_seo_images'], 'permission_callback' => [$this, 'permissions_check'], ]); register_rest_route($this->namespace, '/pos/search', [ 'methods' => 'GET', 'callback' => [$this, 'pos_search'], 'permission_callback' => [$this, 'permissions_check'], ]); } public function get_products($request) { $params = $request->get_params(); $data = $this->repository->get_products($params); return new \WP_REST_Response([ 'success' => true, 'data' => $data['products'], 'total' => $data['total'] ], 200); } public function get_categories() { $categories = get_terms([ 'taxonomy' => 'product_cat', 'hide_empty' => false, ]); if (is_wp_error($categories)) return new \WP_REST_Response(['success' => false, 'data' => []], 200); $data = []; foreach ($categories as $cat) { $data[] = [ 'id' => $cat->term_id, 'name' => $cat->name, 'slug' => $cat->slug, 'count' => $cat->count, ]; } return new \WP_REST_Response([ 'success' => true, 'data' => $data ], 200); } public function get_brands() { $brand_service = \WooCommersa\Services\BrandService::get_instance(); $source = $brand_service->get_configured_source(); if (!$source) return new \WP_REST_Response(['success' => true, 'data' => []], 200); $data = []; if ($source['type'] === 'taxonomy' || $source['type'] === 'attribute') { $terms = get_terms([ 'taxonomy' => $source['slug'], 'hide_empty' => false, ]); if (!is_wp_error($terms)) { foreach ($terms as $brand) { $data[] = [ 'id' => $brand->term_id, 'name' => $brand->name, 'slug' => $brand->slug, 'count' => $brand->count, ]; } } } elseif ($source['type'] === 'meta') { global $wpdb; $meta_values = $wpdb->get_col($wpdb->prepare("SELECT DISTINCT meta_value FROM {$wpdb->postmeta} WHERE meta_key = %s AND meta_value != ''", $source['slug'])); foreach ($meta_values as $val) { $data[] = [ 'id' => $val, 'name' => $val, 'slug' => sanitize_title($val), 'count' => 0 ]; } } return new \WP_REST_Response([ 'success' => true, 'data' => $data, 'source' => $source ], 200); } private function can_edit_product($product_id) { if (current_user_can('manage_woocommerce')) return true; if (function_exists('dokan_is_user_seller') && dokan_is_user_seller(get_current_user_id())) { $author_id = get_post_field('post_author', $product_id); return (int)$author_id === get_current_user_id(); } return false; } public function bulk_update($request) { $params = $request->get_json_params(); $ids = $params['ids'] ?? []; $updates = $params['updates'] ?? []; if (empty($ids) || empty($updates)) { return new \WP_REST_Response(['success' => 0, 'failed' => count($ids)], 200); } // Filter IDs to only those the user can edit $valid_ids = []; foreach ($ids as $id) { if ($this->can_edit_product($id)) { $valid_ids[] = $id; } } if (empty($valid_ids)) { return new \WP_REST_Response(['success' => 0, 'failed' => count($ids)], 200); } $results = $this->bulk_service->apply_updates($valid_ids, $updates); return new \WP_REST_Response($results, 200); } public function batch_save($request) { $params = $request->get_json_params(); $products_data = $params['products'] ?? []; $results = ['success' => 0, 'failed' => 0]; $history = new \WooCommersa\Repositories\HistoryRepository(); $user_id = get_current_user_id(); $batch_id = 'batch_' . time(); foreach ($products_data as $data) { $id = isset($data['id']) ? intval($data['id']) : 0; if (!$this->can_edit_product($id)) { $results['failed']++; continue; } $changes = isset($data['changes']) ? $data['changes'] : []; $product = wc_get_product($id); if (!$product || empty($changes)) { $results['failed']++; continue; } $this->apply_batch_updates($product, $changes, $user_id, $batch_id, $history); // If variable product parent is updated, propagate certain fields to children if ($product->is_type('variable')) { foreach ($product->get_children() as $child_id) { $child = wc_get_product($child_id); if ($child) { $this->apply_batch_updates($child, $changes, $user_id, $batch_id, $history); } } } $results['success']++; } return new \WP_REST_Response($results, 200); } private function apply_batch_updates($product, $data, $user_id, $batch_id, $history) { $id = $product->get_id(); foreach ($data as $key => $value) { if ($key === 'id') continue; $old_val = ''; $changed = false; switch ($key) { case 'name': $old_val = $product->get_name(); if ($old_val != $value) { $product->set_name($value); $changed = true; } break; case 'regular_price': $value = \WooCommersa\Core\Utils::normalize_number($value); $old_val = $product->get_regular_price(); if ($old_val != $value) { $product->set_regular_price($value); $changed = true; } break; case 'sale_price': $value = \WooCommersa\Core\Utils::normalize_number($value); $old_val = $product->get_sale_price(); if ($old_val != $value) { $product->set_sale_price($value); $changed = true; } break; case 'cost_price': $value = \WooCommersa\Core\Utils::normalize_number($value); $old_val = $product->get_meta('_cost_price'); if ($old_val != $value) { $product->update_meta_data('_cost_price', $value); $changed = true; } break; case 'stock': $value = \WooCommersa\Core\Utils::normalize_number($value); $old_val = $product->get_stock_quantity(); if ($old_val != $value) { $product->set_stock_quantity($value); $changed = true; } break; case 'sku': $old_val = $product->get_sku(); if ($old_val != $value) { $product->set_sku($value); $changed = true; } break; case 'stock_status': $old_val = $product->get_stock_status(); if ($old_val != $value) { $product->set_stock_status($value); $changed = true; } break; case 'manage_stock': $old_val = $product->get_manage_stock() ? 'yes' : 'no'; $new_bool = ($value === 'yes' || $value === true || $value === 1 || $value === '1'); if ($old_val != ($new_bool ? 'yes' : 'no')) { $product->set_manage_stock($new_bool); $changed = true; } break; case 'sale_from': $old_val = $product->get_date_on_sale_from() ? $product->get_date_on_sale_from()->date('Y-m-d') : ''; if ($old_val != $value) { $product->set_date_on_sale_from($value); $changed = true; } break; case 'sale_to': $old_val = $product->get_date_on_sale_to() ? $product->get_date_on_sale_to()->date('Y-m-d') : ''; if ($old_val != $value) { $product->set_date_on_sale_to($value); $changed = true; } break; case 'seo_title': case 'seo_description': case 'seo_keywords': case 'seo_robots': $seo_key = str_replace('seo_', '', $key); $old_val = \WooCommersa\Core\SEOAdapter::get_meta($id, $seo_key); \WooCommersa\Core\SEOAdapter::update_meta($id, $seo_key, $value); $history->record($user_id, $id, $key, $old_val, $value, $batch_id, 'batch'); break; case 'faq_schema': $old_val = \WooCommersa\Core\SEOAdapter::get_meta($id, 'faq_schema'); \WooCommersa\Core\SEOAdapter::engine()->meta()->update($id, ['faq_schema' => (array)$value]); $history->record($user_id, $id, $key, maybe_serialize($old_val), maybe_serialize($value), $batch_id, 'batch'); break; case 'seo_redirect': $old_val = get_post_meta($id, '_woocommersa_redirect_url', true); update_post_meta($id, '_woocommersa_redirect_url', esc_url_raw($value)); $history->record($user_id, $id, $key, $old_val, esc_url_raw($value), $batch_id, 'batch'); break; case 'seo_redirect_status': $old_val = get_post_meta($id, '_woocommersa_redirect_status', true); // Handle boolean, string 'true'/'false', or direct 'yes'/'no' $new_status = ($value === true || $value === 'true' || $value === 1 || $value === '1' || $value === 'yes') ? 'yes' : 'no'; update_post_meta($id, '_woocommersa_redirect_status', $new_status); $history->record($user_id, $id, $key, $old_val, $new_status, $batch_id, 'batch'); break; } if ($changed) { $history->record($user_id, $id, $key, $old_val, $value, $batch_id, 'batch'); } } $product->save(); // Trigger Profit & Margin recalculation $pm_service = new \WooCommersa\Services\Profit_Margin_Service(); $pm_service->calculate_metrics($id); } public function get_batch_variations($request) { $parent_ids = $request->get_param('parent_ids'); if (empty($parent_ids)) return new \WP_REST_Response([], 200); $ids = array_map('intval', explode(',', $parent_ids)); $results = []; foreach ($ids as $id) { $product = wc_get_product($id); if (!$product || !$product->is_type('variable')) continue; $variations = $product->get_children(); $var_data = []; foreach ($variations as $var_id) { $v = wc_get_product($var_id); if (!$v) continue; $var_data[] = $this->repository->map_product_data($v); } $results[$id] = $var_data; } return new \WP_REST_Response($results, 200); } public function get_product_variations($request) { $id = $request['id']; $product = wc_get_product($id); if (!$product || $product->get_type() !== 'variable') { return new \WP_REST_Response([], 200); } $variations_data = []; foreach ($product->get_children() as $child_id) { $v = wc_get_product($child_id); if (!$v) continue; $variations_data[] = [ 'id' => $v->get_id(), 'name' => $v->get_name(), 'sku' => $v->get_sku(), 'regular_price' => $v->get_regular_price(), 'sale_price' => $v->get_sale_price(), 'stock' => $v->get_stock_quantity(), 'stock_status' => $v->get_stock_status(), 'manage_stock' => $v->get_manage_stock(), 'type' => 'variation', 'parent_id' => $id, 'image' => wp_get_attachment_image_url($v->get_image_id(), 'thumbnail'), 'categories' => wp_get_post_terms($id, 'product_cat', ['fields' => 'names']), 'cost_price' => get_post_meta($v->get_id(), '_cost_price', true), 'attributes' => $this->get_formatted_variation_attributes($v), 'sale_from' => $v->get_date_on_sale_from() ? $v->get_date_on_sale_from()->date('Y-m-d') : '', 'sale_to' => $v->get_date_on_sale_to() ? $v->get_date_on_sale_to()->date('Y-m-d') : '', ]; } return new \WP_REST_Response($variations_data, 200); } private function get_formatted_variation_attributes($variation) { $attributes = $variation->get_attributes(); $formatted = []; foreach ($attributes as $key => $value) { $taxonomy = str_replace('attribute_', '', $key); if (taxonomy_exists($taxonomy)) { $term = get_term_by('slug', $value, $taxonomy); $formatted[$key] = $term ? $term->name : urldecode($value); } else { $formatted[$key] = urldecode($value); } } return $formatted; } public function update_product($request) { $params = $request->get_json_params(); $id = $params['id'] ?? 0; if (!$this->can_edit_product($id)) { return new \WP_REST_Response(['success' => false, 'message' => 'Access denied'], 403); } $product = wc_get_product($id); if (!$product) return new \WP_REST_Response(['success' => false, 'message' => 'Product not found'], 404); $field = $params['field'] ?? ''; $value = $params['value'] ?? ''; $history = new \WooCommersa\Repositories\HistoryRepository(); $user_id = get_current_user_id(); $batch_id = 'manual_' . time(); // If field/value is provided directly (new format) if ($field) { $this->apply_single_update($product, $field, $value, $user_id, $batch_id, $history); } else { // Legacy format or bulk object foreach ($params as $key => $val) { if ($key === 'id') continue; $this->apply_single_update($product, $key, $val, $user_id, $batch_id, $history); } } $product->save(); return new \WP_REST_Response(['success' => true], 200); } private function apply_single_update($product, $field, $value, $user_id = 0, $batch_id = '', $history = null) { $id = $product->get_id(); $old_val = ''; $changed = false; switch ($field) { case 'regular_price': $old_val = $product->get_regular_price(); $product->set_regular_price($value); $changed = ($old_val != $value); break; case 'sale_price': $old_val = $product->get_sale_price(); $product->set_sale_price($value); $changed = ($old_val != $value); break; case 'stock': $old_val = $product->get_stock_quantity(); $product->set_stock_quantity($value); $changed = ($old_val != $value); break; case 'sku': $old_val = $product->get_sku(); $product->set_sku($value); $changed = ($old_val != $value); break; case 'name': $old_val = $product->get_name(); $product->set_name($value); $changed = ($old_val != $value); break; case 'status': $old_val = $product->get_status(); $product->set_status($value); $changed = ($old_val != $value); break; case 'stock_status': $old_val = $product->get_stock_status(); $product->set_stock_status($value); $changed = ($old_val != $value); break; case 'manage_stock': $old_val = $product->get_manage_stock() ? 'yes' : 'no'; $new_val = ($value === 'yes' || $value === true || $value === 1 || $value === '1') ? 'yes' : 'no'; $product->set_manage_stock($new_val === 'yes'); $changed = ($old_val != $new_val); break; case 'cost_price': $old_val = $product->get_meta('_cost_price'); $product->update_meta_data('_cost_price', $value); $changed = ($old_val != $value); break; case 'sale_from': $old_val = $product->get_date_on_sale_from() ? $product->get_date_on_sale_from()->date('Y-m-d') : ''; $product->set_date_on_sale_from($value); $changed = ($old_val != $value); break; case 'sale_to': $old_val = $product->get_date_on_sale_to() ? $product->get_date_on_sale_to()->date('Y-m-d') : ''; $product->set_date_on_sale_to($value); $changed = ($old_val != $value); break; case 'categories': $cat_ids = is_array($value) ? $value : explode(',', $value); $cat_ids = array_map('intval', array_filter($cat_ids)); wp_set_object_terms($id, $cat_ids, 'product_cat'); // Note: categories rollback not fully supported in HistoryRepository Switch yet, but we log it $changed = true; break; case 'seo_title': \WooCommersa\Core\SEOAdapter::update_meta($id, 'title', $value); $changed = true; break; case 'seo_description': \WooCommersa\Core\SEOAdapter::update_meta($id, 'description', $value); $changed = true; break; case 'seo_keywords': \WooCommersa\Core\SEOAdapter::update_meta($id, 'keywords', $value); $changed = true; break; case 'seo_robots': \WooCommersa\Core\SEOAdapter::update_meta($id, 'robots', $value); $changed = true; break; case 'faq_schema': \WooCommersa\Core\SEOAdapter::engine()->meta()->update($id, ['faq_schema' => (array)$value]); $changed = true; break; case 'seo_redirect': update_post_meta($id, '_woocommersa_redirect_url', $value); $changed = true; break; case 'seo_redirect_status': update_post_meta($id, '_woocommersa_redirect_status', ($value ? 'yes' : 'no')); $changed = true; break; case 'margin': case 'profit': case 'regular_price_by_margin': $cost = floatval(get_post_meta($id, '_cost_price', true)); $settings = get_option('woocommersa_settings', []); $oh_dec = floatval($settings['profit_overhead_percent'] ?? 0) / 100; if ($field === 'margin' || $field === 'regular_price_by_margin') { $margin_dec = floatval($value) / 100; $denominator = (1 - $oh_dec - $margin_dec); if ($denominator > 0) { $value = round($cost / $denominator); $product->set_regular_price($value); $changed = true; $field = 'regular_price'; } } elseif ($field === 'profit') { if (1 - $oh_dec > 0) { $value = round(($cost + floatval($value)) / (1 - $oh_dec)); $product->set_regular_price($value); $changed = true; $field = 'regular_price'; } } break; case 'product_tags': $tags = is_array($value) ? $value : explode(',', $value); $tags = array_map('trim', array_filter($tags)); wp_set_object_terms($id, $tags, 'product_tag'); $changed = true; break; } // Trigger Profit & Margin recalculation if financial fields changed if (in_array($field, ['regular_price', 'sale_price', 'cost_price'])) { $pm_service = new \WooCommersa\Services\Profit_Margin_Service(); $pm_service->calculate_metrics($id); } wp_cache_delete($id, 'post_meta'); // Trigger hooks for SEO plugins do_action('save_post', $id, get_post($id), true); do_action('woocommerce_update_product', $id, $product); if ($changed && $history) { $history->record($user_id, $id, $field, $old_val, $value, $batch_id, 'manual'); } // Propagate to children if parent is updated if ($product->is_type('variable') && in_array($field, ['regular_price', 'sale_price', 'stock', 'stock_status', 'manage_stock', 'sale_from', 'sale_to'])) { foreach ($product->get_children() as $child_id) { $child = wc_get_product($child_id); if ($child) { $this->apply_single_update($child, $field, $value, $user_id, $batch_id, $history); $child->save(); } } } } public function get_bulk_images($request) { global $wpdb; $params = $request->get_params(); $per_page = intval($params['per_page'] ?? 25); $page = intval($params['page'] ?? 1); $offset = ($page - 1) * $per_page; $missing_alt = !empty($params['missing_alt']) && $params['missing_alt'] === 'true'; $missing_product_image = !empty($params['missing_product_image']) && $params['missing_product_image'] === 'true'; $non_square = !empty($params['non_square']) && $params['non_square'] === 'true'; $min_size_kb = !empty($params['min_size']) ? intval($params['min_size']) : 0; $search = !empty($params['search']) ? sanitize_text_field($params['search']) : ''; $category = !empty($params['category']) ? sanitize_text_field($params['category']) : ''; // Handle Products without images separately if ($missing_product_image) { $query = "SELECT p.ID, p.post_title, p.post_content, p.post_excerpt, p.post_parent, p.post_date FROM {$wpdb->posts} p WHERE p.post_type = 'product' AND p.post_status = 'publish' AND p.ID NOT IN (SELECT post_id FROM {$wpdb->postmeta} WHERE meta_key = '_thumbnail_id')"; if ($search) { if (strpos($search, 'id:') === 0) { $query .= $wpdb->prepare(" AND p.ID = %d", intval(substr($search, 3))); } else { $query .= $wpdb->prepare(" AND p.post_title LIKE %s", '%' . $wpdb->esc_like($search) . '%'); } } if ($category) { $query .= $wpdb->prepare(" AND p.ID 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 = 'product_cat' AND tt.term_id = (SELECT term_id FROM {$wpdb->terms} WHERE slug = %s LIMIT 1))", $category); } $query .= " ORDER BY p.ID DESC"; $results = $wpdb->get_results($query); $total_count = count($results); $results = array_slice($results, $offset, $per_page); $data = []; foreach ($results as $p) { $data[] = [ 'id' => 0, 'preview' => '', 'full' => '', 'filename' => 'فاقد تصویر', 'parent_product' => $p->post_title, 'parent_id' => $p->ID, 'title' => '', 'alt' => '', 'caption' => '', 'description' => '', 'size' => '0 KB', 'size_raw' => 0, 'width' => 0, 'height' => 0, 'date' => $p->post_date, 'edit_link' => get_edit_post_link($p->ID, 'raw'), ]; } return new \WP_REST_Response([ 'images' => $data, 'total' => $total_count, 'pages' => ceil($total_count / $per_page) ], 200); } // Base Query: Fetch attachments that are images $query = "SELECT p.ID, p.post_title, p.post_content, p.post_excerpt, p.post_parent, p.post_date FROM {$wpdb->posts} p WHERE p.post_type = 'attachment' AND p.post_mime_type LIKE 'image/%'"; if ($missing_alt) { $query .= " AND p.ID NOT IN (SELECT post_id FROM {$wpdb->postmeta} WHERE meta_key = '_wp_attachment_image_alt' AND meta_value != '')"; } if ($search) { if (strpos($search, 'id:') === 0) { $query .= $wpdb->prepare(" AND p.ID = %d", intval(substr($search, 3))); } else { $query .= $wpdb->prepare(" AND (p.post_title LIKE %s OR p.ID IN (SELECT parent.ID FROM {$wpdb->posts} parent WHERE parent.post_type = 'product' AND parent.post_title LIKE %s))", '%' . $wpdb->esc_like($search) . '%', '%' . $wpdb->esc_like($search) . '%'); } } if ($category) { $query .= $wpdb->prepare(" AND p.post_parent 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 = 'product_cat' AND tt.term_id = (SELECT term_id FROM {$wpdb->terms} WHERE slug = %s LIMIT 1))", $category); } $query .= " ORDER BY p.ID DESC"; $attachments = $wpdb->get_results($query); $data = []; $found_in_meta = 0; foreach ($attachments as $att) { // Meta-based filtering in loop $meta = wp_get_attachment_metadata($att->ID); $file_path = get_attached_file($att->ID); $size_raw = @filesize($file_path); $size_kb = $size_raw ? round($size_raw / 1024, 1) : 0; // Filter: Min Size if ($min_size_kb > 0 && $size_kb < $min_size_kb) continue; // Filter: Non-Square if ($non_square && isset($meta['width'], $meta['height']) && $meta['width'] === $meta['height']) continue; // Pagination Logic if ($found_in_meta < $offset) { $found_in_meta++; continue; } if (count($data) >= $per_page) break; $parent_name = ''; if ($att->post_parent) { $parent_name = get_the_title($att->post_parent); } else { $parent_id = $wpdb->get_var($wpdb->prepare("SELECT post_id FROM {$wpdb->postmeta} WHERE (meta_key = '_thumbnail_id' OR meta_key = '_product_image_gallery') AND meta_value LIKE %s LIMIT 1", '%' . $att->ID . '%')); if ($parent_id) $parent_name = get_the_title($parent_id); } $data[] = [ 'id' => $att->ID, 'preview' => wp_get_attachment_image_url($att->ID, 'thumbnail') ?: wp_get_attachment_url($att->ID), 'full' => wp_get_attachment_url($att->ID), 'filename' => basename($file_path), 'parent_product' => $parent_name, 'parent_id' => $att->post_parent, 'title' => $att->post_title, 'alt' => get_post_meta($att->ID, '_wp_attachment_image_alt', true), 'caption' => $att->post_excerpt, 'description' => $att->post_content, 'size' => $size_raw ? $size_kb . ' KB' : 'Unknown', 'size_raw' => $size_kb, 'width' => $meta['width'] ?? 0, 'height' => $meta['height'] ?? 0, 'date' => $att->post_date, 'edit_link' => get_edit_post_link($att->ID, 'raw'), ]; } return new \WP_REST_Response([ 'images' => $data, 'total' => count($attachments), 'pages' => ceil(count($attachments) / $per_page) ], 200); } public function update_bulk_images($request) { $params = $request->get_json_params(); $changes = $params['changes'] ?? []; $success = 0; foreach ($changes as $change) { $id = intval($change['id']); $field = $change['field']; $value = $change['value']; switch ($field) { case 'title': wp_update_post(['ID' => $id, 'post_title' => $value]); $success++; break; case 'alt': update_post_meta($id, '_wp_attachment_image_alt', $value); $success++; break; case 'caption': wp_update_post(['ID' => $id, 'post_excerpt' => $value]); $success++; break; case 'description': wp_update_post(['ID' => $id, 'post_content' => $value]); $success++; break; } } return new \WP_REST_Response(['success' => true, 'count' => $success], 200); } public function replace_image($request) { $id = intval($request->get_param('id')); $product_id = intval($request->get_param('product_id')); $files = $request->get_file_params(); if (empty($files['image'])) { return new \WP_REST_Response(['success' => false, 'message' => 'No image uploaded'], 400); } require_once ABSPATH . 'wp-admin/includes/image.php'; require_once ABSPATH . 'wp-admin/includes/file.php'; require_once ABSPATH . 'wp-admin/includes/media.php'; // Get parent product $parent_id = $id ? wp_get_post_parent_id($id) : $product_id; $attachment_id = media_handle_upload('image', $parent_id); if (is_wp_error($attachment_id)) { return new \WP_REST_Response(['success' => false, 'message' => $attachment_id->get_error_message()], 500); } // If we want to strictly REPLACING the same ID, it's harder in WP. // Usually we just upload a new one and update the association. if ($parent_id) { $product = wc_get_product($parent_id); if ($product) { $product->set_image_id($attachment_id); $product->save(); // Optional: Delete old image wp_delete_attachment($id, true); } } return new \WP_REST_Response([ 'success' => true, 'id' => $attachment_id, 'preview' => wp_get_attachment_image_url($attachment_id, 'thumbnail'), 'full' => wp_get_attachment_url($attachment_id) ], 200); } public function auto_seo_images($request) { $params = $request->get_json_params(); $ids = $params['ids'] ?? []; $pattern = $params['pattern'] ?? 'خرید {product_name}'; $count = 0; foreach ($ids as $image_id) { $parent_id = wp_get_post_parent_id($image_id); if (!$parent_id) { // If not directly parented, try to find product that uses it as featured image global $wpdb; $parent_id = $wpdb->get_var($wpdb->prepare("SELECT post_id FROM {$wpdb->postmeta} WHERE meta_key = '_thumbnail_id' AND meta_value = %d LIMIT 1", $image_id)); } if ($parent_id) { $product = wc_get_product($parent_id); if ($product) { $alt_text = str_replace('{product_name}', $product->get_name(), $pattern); update_post_meta($image_id, '_wp_attachment_image_alt', $alt_text); $count++; } } } return new \WP_REST_Response(['success' => true, 'count' => $count], 200); } public function pos_search($request) { $search = $request->get_param('search'); $args = [ 'per_page' => 24, 'status' => 'publish' ]; if (empty($search)) { // Default View: Recent Products $args['orderby'] = 'date'; $args['order'] = 'DESC'; } else { $args['search'] = $search; } // Use repository for searching $results = $this->repository->get_products($args); $data = []; foreach ($results['products'] as $p_data) { $product = wc_get_product($p_data['id']); if ($product) { $data[] = $this->map_pos_product($product); } } return new \WP_REST_Response([ 'success' => true, 'data' => $data ], 200); } private function map_pos_product($product) { $id = $product->get_id(); $data = [ 'id' => $id, 'name' => $product->get_name(), 'sku' => $product->get_sku(), 'price' => $product->get_price(), 'regular_price' => $product->get_regular_price(), 'sale_price' => $product->get_sale_price(), 'stock' => $product->get_stock_quantity(), 'stock_status' => $product->get_stock_status(), 'image' => wp_get_attachment_image_url($product->get_image_id(), 'thumbnail'), 'type' => $product->get_type(), ]; if ($product->is_type('variable')) { $variations = []; foreach ($product->get_children() as $var_id) { $v = wc_get_product($var_id); if ($v) { $variations[] = [ 'id' => $v->get_id(), 'name' => $v->get_name(), 'sku' => $v->get_sku(), 'price' => $v->get_price(), 'attributes' => $this->get_formatted_variation_attributes($v), 'stock' => $v->get_stock_quantity(), 'stock_status' => $v->get_stock_status(), ]; } } $data['variations'] = $variations; } return $data; } }