Файловый менеджер - Редактировать - /home/tuudkjt/globeasy/wp-includes/ID3/Services.zip
Назад
PK �N,]���U U CategoryService.phpnu ��� <?php namespace Rvx\Services; use Rvx\Apiz\Http\Response; use Rvx\WPDrill\Response as drillResponse; use Rvx\Api\CategoryApi; use Rvx\Utilities\Helper; class CategoryService extends \Rvx\Services\Service { /** * */ public function __construct() { // add_action('save_post', [$this, 'saveProduct'], 10, 1); } /** * @return Response */ public function selectable() { return (new CategoryApi())->selectable(); } /** * @return Response */ public function getCategory() { return (new CategoryApi())->getCategory(); } /** * @return Response */ public function getCategoryAll() : drillResponse { $product_categories = get_terms(array('taxonomy' => 'product_cat', 'hide_empty' => \false)); $response = array(); foreach ($product_categories as $parent_category) { $subcategory_array = array(); $subcategories = get_terms(array('taxonomy' => 'product_cat', 'hide_empty' => \false, 'parent' => $parent_category->term_id)); foreach ($subcategories as $subcategory) { $subcategory_array[] = array('name' => $subcategory->name, 'id' => $subcategory->term_id); } $response[] = array('parent' => array('name' => $parent_category->name, 'id' => $parent_category->term_id), 'subcategories' => $subcategory_array); } return Helper::rest($response)->success("All category list"); } public function storeCategory($data) { return (new CategoryApi())->create($data); } } PK �N,](��' ' ProductService.phpnu ��� <?php namespace Rvx\Services; use Rvx\Apiz\Http\Response; use Rvx\Api\ProductApi; class ProductService extends \Rvx\Services\Service { /** * @return Response */ public function getSelectProduct($data) { return (new ProductApi())->getProductSelect($data); } } PK �N,]�Z��� � PingService.phpnu ��� <?php namespace Rvx\Services; class PingService extends \Rvx\Services\Service { public function __construct() { } /** * Ping the WP to check the status of the plugin and server. * * @return array */ public function ping() { global $wpdb, $wp_version; // Get migration status $migration_status = \get_option('_rvx_db_upgrade_216', '0'); $rollback_status = \get_option('_rvx_current_rollback', '0'); return ['environment' => ['php' => ['version' => \PHP_VERSION, 'memory_limit' => \ini_get('memory_limit'), 'max_execution_time' => \ini_get('max_execution_time'), 'extensions' => \get_loaded_extensions(), 'opcache_enabled' => \extension_loaded('opcache') && \opcache_get_status()['opcache_enabled'], 'apcu_enabled' => \extension_loaded('apcu') && \ini_get('apc.enabled')], 'server' => ['software' => $_SERVER['SERVER_SOFTWARE'] ?? 'N/A', 'protocol' => $_SERVER['SERVER_PROTOCOL'] ?? 'N/A', 'https' => is_ssl(), 'db_version' => $wpdb->db_version(), 'fs_method' => \defined('FS_METHOD') ? FS_METHOD : 'direct']], 'wordpress' => ['version' => $wp_version, 'locale' => get_locale(), 'multisite' => is_multisite(), 'debug' => ['wp_debug' => WP_DEBUG, 'wp_debug_log' => WP_DEBUG_LOG, 'wp_debug_display' => WP_DEBUG_DISPLAY, 'script_debug' => SCRIPT_DEBUG], 'memory' => ['wp_memory_limit' => WP_MEMORY_LIMIT, 'wp_max_memory_limit' => WP_MAX_MEMORY_LIMIT, 'current_usage' => \round(\memory_get_usage() / 1024 / 1024, 2) . 'M'], 'cron' => ['jobs_count' => \count(_get_cron_array()), 'alternate_wp_cron' => \defined('ALTERNATE_WP_CRON') && ALTERNATE_WP_CRON], 'cache' => $this->get_cache_status()], 'reviewx' => ['version' => RVX_VERSION, 'migration' => ['status' => (bool) $migration_status], 'rollback' => ['status' => (bool) $rollback_status], 'paths' => ['dir_name' => RVX_DIR_NAME, 'dir_path' => RVX_DIR_PATH, 'dir_url' => RVX_URL]], 'plugins' => $this->get_plugins_status(), 'theme' => $this->get_theme_details()]; } private function get_cache_status() { return ['wp_cache' => \defined('WP_CACHE') && WP_CACHE, 'object_cache' => \file_exists(WP_CONTENT_DIR . '/object-cache.php'), 'browser_cache' => (bool) \get_option('gzipcompression', 0), 'plugins' => $this->detect_caching_plugins(), 'server_side' => $this->detect_server_caching()]; } private function detect_caching_plugins() { $caching_plugins = []; $active_plugins = \get_option('active_plugins', []); $known_caching_plugins = ['w3-total-cache' => 'w3-total-cache/w3-total-cache.php', 'wp-super-cache' => 'wp-super-cache/wp-cache.php', 'wp-rocket' => 'wp-rocket/wp-rocket.php', 'litespeed-cache' => 'litespeed-cache/litespeed-cache.php', 'autoptimize' => 'autoptimize/autoptimize.php']; foreach ($known_caching_plugins as $name => $plugin_path) { if (\in_array($plugin_path, $active_plugins)) { $caching_plugins[$name] = \true; } } return $caching_plugins; } private function detect_server_caching() { $headers = \headers_list(); $server_software = $_SERVER['SERVER_SOFTWARE'] ?? ''; return ['varnish' => isset($_SERVER['HTTP_X_VARNISH']), 'nginx_cache' => \strpos($server_software, 'nginx') !== \false, 'cloudflare' => isset($_SERVER['HTTP_CF_RAY']), 'opcache' => \extension_loaded('opcache') && \opcache_get_status()['opcache_enabled'], 'redis' => \defined('WP_REDIS_HOST'), 'memcached' => \defined('WP_MEMCACHED_HOST')]; } private function test_rest_api() { $response = wp_remote_get(rest_url('wp/v2/types/post')); return ['status' => !\is_wp_error($response), 'response_code' => wp_remote_retrieve_response_code($response), 'error' => \is_wp_error($response) ? $response->get_error_message() : null]; } private function get_theme_details() { $theme = wp_get_theme(); return ['name' => $theme->get('Name'), 'version' => $theme->get('Version'), 'parent_theme' => $theme->parent() ? $theme->parent()->get('Name') : null, 'theme_uri' => $theme->get('ThemeURI'), 'author' => $theme->get('Author'), 'template_dir' => $theme->get_template_directory(), 'stylesheet_dir' => $theme->get_stylesheet_directory()]; } private function get_plugins_status() { if (!\function_exists('Rvx\\get_plugins')) { require_once ABSPATH . 'wp-admin/includes/plugin.php'; } $plugins = []; foreach (get_plugins() as $path => $plugin) { if (is_plugin_active($path)) { $plugins[$plugin['TextDomain'] ?? $path] = ['version' => $plugin['Version'], 'network_active' => is_plugin_active_for_network($path), 'author' => $plugin['Author'], 'update_available' => $this->check_plugin_update($path, $plugin)]; } } return $plugins; } private function check_plugin_update($plugin_path, $plugin_data) { $updates = \get_transient('update_plugins'); return isset($updates->response[$plugin_path]) ? $updates->response[$plugin_path]->new_version : \false; } } PK �N,]�G���) �) ProductSyncService.phpnu ��� <?php namespace Rvx\Services; use Exception; use Rvx\Utilities\Helper; use Rvx\WPDrill\Facades\DB; use Rvx\Services\CategorySyncService; class ProductSyncService extends \Rvx\Services\Service { protected $postMetaPriceRelation; protected $productCount = 0; protected $postMetaAverageRatingRelation; protected $postMetaRatingCountPercentageRelation; protected $postMetaReviewsCountRelation; protected $postMetaSalePriceRelation; protected $postMetaThumbnaiRelation; protected $postMetaAttachmentsRelation; protected $postMetaStarCountsRelation; protected $productids; protected $postAttachmentRelation; protected CategorySyncService $syncedCategories; protected $postTermRelation; public function __construct() { $this->syncedCategories = new CategorySyncService(); $this->postTermRelation = $this->syncedCategories->getPostTermRelation(); } public function processProductForSync($file, $post_type) : int { $this->syncProductsMeta($post_type); return $this->syncProducts($file, $post_type); } public function getProductAttachementRalation() { return $this->postAttachmentRelation; } public function syncProductsMeta($post_type) { // Base meta keys $dbTableKeys = ['rvx_avg_rating', 'rating', '_thumbnail_id', 'rvx_total_reviews', 'rvx_star_count_1', 'rvx_star_count_2', 'rvx_star_count_3', 'rvx_star_count_4', 'rvx_star_count_5']; // Add product-specific keys if ($post_type === 'product') { $dbTableKeys = \array_merge($dbTableKeys, ['_price', '_sale_price', '_wc_review_count', '_wc_average_rating', '_wc_rating_count']); } // Define relation targets by meta key $relationMap = ['_price' => 'postMetaPriceRelation', '_sale_price' => 'postMetaSalePriceRelation', '_wc_review_count' => 'postMetaReviewsCountRelation', '_wc_rating_count' => 'postMetaRatingCountPercentageRelation', '_thumbnail_id' => 'postMetaThumbnaiRelation', 'rvx_total_reviews' => 'postMetaReviewsCountRelation']; try { DB::table('postmeta')->whereIn('meta_key', $dbTableKeys)->chunk(100, function ($allPostMeta) use($relationMap) { foreach ($allPostMeta as $meta) { $key = $meta->meta_key; $pid = $meta->post_id; $value = $meta->meta_value; // Direct assignment using map if (isset($relationMap[$key])) { $this->{$relationMap[$key]}[$pid] = $value; continue; } // Collect Star Counts for CPT if (\strpos($key, 'rvx_star_count_') === 0) { $starIndex = \str_replace('rvx_star_count_', '', $key); // 1, 2, 3... $this->postMetaStarCountsRelation[$pid][$starIndex] = $value; continue; } // --- Rating Prioritization Logic --- // Priority: _wc_average_rating > rvx_avg_rating > rating if ($key === '_wc_average_rating' || $key === 'rvx_avg_rating' || $key === 'rating') { // Assign only if not already assigned by a higher-priority field if (!isset($this->postMetaAverageRatingRelation[$pid])) { $this->postMetaAverageRatingRelation[$pid] = $value; } } } }); } catch (Exception $e) { throw new Exception($e->getMessage()); } } public function syncProducts($file, $post_type) { $productCount = 0; $attachmentRelation = []; $this->postMetaAttachmentsRelation = []; DB::table('posts')->select(['ID', 'post_type', 'post_title', 'post_name', 'post_excerpt', 'post_status', 'guid', 'post_modified', 'comment_count'])->orderBy('ID')->whereIn('post_type', [$post_type])->chunk(100, function ($products) use(&$attachmentRelation, &$file, &$productCount) { foreach ($products as $product) { $this->productids[] = $product->ID; $productImage = get_the_post_thumbnail_url($product->ID, 'full') ? get_the_post_thumbnail_url($product->ID, 'full') : null; if ($product->post_type !== 'product') { $this->postMetaReviewsCountRelation[$product->ID] = $product->comment_count; } $formatedProduct = $this->processProduct($product, $productImage); if ($formatedProduct['post_type'] !== 'attachment') { Helper::appendToJsonl($file, $formatedProduct); $productCount++; } } }); $this->setPostAttachemtRelation($attachmentRelation); Helper::rvxLog($productCount, "Product Done"); return $productCount; } public function setPostAttachemtRelation($attachmentRelation) : void { $this->postAttachmentRelation = $attachmentRelation; } public function processProduct($product, $productImage) : array { $reviewsCount = isset($this->postMetaReviewsCountRelation[$product->ID]) ? (int) $this->postMetaReviewsCountRelation[$product->ID] : 0; $ratingCount = $this->postMetaRatingCountPercentageRelation[$product->ID] ?? []; // Handle CPT Star Counts (from rvx_ meta) if ($product->post_type !== 'product') { // Self-Healing: If star data is missing, calculate it NOW. if (!isset($this->postMetaStarCountsRelation[$product->ID])) { \Rvx\CPT\CptAverageRating::update_average_rating($product->ID); // Fetch fresh data immediately $freshStars = []; for ($i = 1; $i <= 5; $i++) { $freshStars[$i] = (int) \get_post_meta($product->ID, "rvx_star_count_{$i}", \true); } $this->postMetaStarCountsRelation[$product->ID] = $freshStars; $freshTotal = (int) \get_post_meta($product->ID, 'rvx_total_reviews', \true); $this->postMetaReviewsCountRelation[$product->ID] = $freshTotal; $freshAvg = (float) \get_post_meta($product->ID, 'rvx_avg_rating', \true); $this->postMetaAverageRatingRelation[$product->ID] = $freshAvg; } $cptStars = $this->postMetaStarCountsRelation[$product->ID]; // Format to match what ratingCountsConverter expects or construct directly $ratingCounts = ["one" => (int) ($cptStars[1] ?? 0), "two" => (int) ($cptStars[2] ?? 0), "three" => (int) ($cptStars[3] ?? 0), "four" => (int) ($cptStars[4] ?? 0), "five" => (int) ($cptStars[5] ?? 0)]; // Refresh total reviews count from relation if it was updated $reviewsCount = isset($this->postMetaReviewsCountRelation[$product->ID]) ? (int) $this->postMetaReviewsCountRelation[$product->ID] : $reviewsCount; } else { // Default WooCommerce logic // Ensure WooCommerce serialized rating count is converted to array if (\is_string($ratingCount)) { $decoded = @\unserialize($ratingCount); $ratingCount = \is_array($decoded) ? $decoded : []; } $ratingCounts = $this->ratingCountsConverter($ratingCount); } return ['rid' => 'rid://Product/' . (int) $product->ID, "post_type" => $product->post_type ?? null, "wp_id" => (int) ($product->ID ?? 0), "title" => isset($product->post_title) ? \htmlspecialchars($product->post_title, \ENT_QUOTES, 'UTF-8') : null, "url" => $product->guid ?? '', "description" => $product->post_excerpt ?? null, "price" => isset($this->postMetaPriceRelation[$product->ID]) ? Helper::formatToTwoDecimalPlaces($this->postMetaPriceRelation[$product->ID]) : 0, "discounted_price" => isset($this->postMetaSalePriceRelation[$product->ID]) ? Helper::formatToTwoDecimalPlaces($this->postMetaSalePriceRelation[$product->ID]) : 0, "slug" => $product->post_name ?? '', "status" => $this->productStatus($product->post_status ?? ''), "total_reviews" => $reviewsCount, "avg_rating" => isset($this->postMetaAverageRatingRelation[$product->ID]) ? Helper::formatToTwoDecimalPlaces($this->postMetaAverageRatingRelation[$product->ID]) : 0, "stars" => ["one" => $ratingCounts["one"], "two" => $ratingCounts["two"], "three" => $ratingCounts["three"], "four" => $ratingCounts["four"], "five" => $ratingCounts["five"]], "one_stars" => $ratingCounts["one"], "two_stars" => $ratingCounts["two"], "three_stars" => $ratingCounts["three"], "four_stars" => $ratingCounts["four"], "five_stars" => $ratingCounts["five"], "modified_date" => Helper::validateReturnDate($product->post_modified) ?? null, "image" => $productImage, "category_ids" => isset($this->postTermRelation[(int) $product->ID]) && \is_array($this->postTermRelation[(int) $product->ID]) ? \array_map('intval', $this->postTermRelation[(int) $product->ID]) : []]; } private function ratingCountsConverter(array $ratingCount) : array { // Final output initialized to 0 $stars = ["one" => 0, "two" => 0, "three" => 0, "four" => 0, "five" => 0]; // Empty OR invalid input → return default immediately if (empty($ratingCount)) { return $stars; } foreach ($ratingCount as $rawKey => $value) { // Convert numeric strings $value = \is_numeric($value) ? (float) $value : 0; // Convert key to float (handles "3.5", 5, "4", etc.) $key = \is_numeric($rawKey) ? (float) $rawKey : null; if ($key === null) { continue; } // Round decimals down (3.5 => 3) $bucket = (int) \floor($key); switch ($bucket) { case 1: $stars["one"] += $value; break; case 2: $stars["two"] += $value; break; case 3: $stars["three"] += $value; break; case 4: $stars["four"] += $value; break; case 5: $stars["five"] += $value; break; } } return $stars; } public function productStatus($status) : int { switch ($status) { case 'publish': return 1; case 'private': return 2; default: return 3; } } } PK �N,]�)h� � OrderItemSyncService.phpnu ��� <?php namespace Rvx\Services; use DateTime; use Rvx\Utilities\Auth\Client; use Rvx\Utilities\Helper; use Rvx\WPDrill\Facades\DB; class OrderItemSyncService extends \Rvx\Services\Service { protected $orderFullfillmentStatusRelation; protected $validOrderIds = []; protected $validOrdersMetaIds = []; protected $orderItems = []; protected $orderItemCount = 0; protected $orderFullfillmentAtRelation; protected $orderItemOrderRelation = []; protected $orderItemProductRelation = []; protected $orderItemQtyRelation = []; protected $orderItemPriceRelation = []; public function syncOrder($file) : int { $orderCount = 0; $this->orderStat(); $startDate = (new DateTime())->modify('-60 days')->format('Y-m-d H:i:s'); $endDate = (new DateTime())->format('Y-m-d H:i:s'); DB::table('wc_orders')->select(['id', 'customer_id', 'total_amount', 'tax_amount', 'status', 'date_created_gmt', 'date_updated_gmt'])->whereBetween('date_created_gmt', $startDate, $endDate)->chunk(100, function ($orders) use($file, &$orderCount) { foreach ($orders as $order) { $this->validOrderIds[] = (int) $order->id; $order->fulfillment_status = $this->orderFullfillmentStatusRelation[(int) $order->id] ?? null; $order->fulfilled_at = $this->orderFullfillmentAtRelation[(int) $order->id] ?? null; $formattedOrder = $this->formatOrderData($order); Helper::appendToJsonl($file, $formattedOrder); $orderCount++; } }); Helper::rvxLog($orderCount, "Order Done"); return $orderCount; } public function formatOrderData($order) : array { $paid_at = !empty($order->fulfilled_at) && \strtotime($order->fulfilled_at) ? \wp_date('Y-m-d H:i:s', \strtotime($order->fulfilled_at)) : null; return ['rid' => 'rid://Order/' . (int) $order->id, 'wp_id' => (int) $order->id, 'customer_wp_unique_id' => $order->customer_id ? Client::getUid() . '-' . $order->customer_id : null, 'subtotal' => Helper::formatToTwoDecimalPlaces($order->total_amount ?? 0.0), 'tax' => Helper::formatToTwoDecimalPlaces($order->tax_amount ?? 0.0), 'total' => Helper::formatToTwoDecimalPlaces($order->total_amount ?? 0.0), 'status' => isset($order->status) ? Helper::orderStatus(Helper::rvxGetOrderStatus($order->status)) : null, 'review_request_email_sent_at' => null, 'review_reminder_email_sent_at' => null, 'photo_review_email_sent_at' => null, 'paid_at' => $paid_at, 'created_at' => !empty($order->date_created_gmt) ? Helper::validateReturnDate($order->date_created_gmt) : null, 'updated_at' => !empty($order->date_updated_gmt) ? Helper::validateReturnDate($order->date_updated_gmt) : null]; } public function orderStat() { $startDate = (new DateTime())->modify('-60 days')->format('Y-m-d H:i:s'); $endDate = (new DateTime())->format('Y-m-d H:i:s'); DB::table('wc_order_stats')->whereBetween('date_created', $startDate, $endDate)->chunk(100, function ($orderStats) { foreach ($orderStats as $orderStat) { $this->orderFullfillmentStatusRelation[(int) $orderStat->order_id] = Helper::orderItemStatus(Helper::rvxGetOrderStatus($orderStat->status)); $this->orderFullfillmentAtRelation[(int) $orderStat->order_id] = $orderStat->date_completed ?? $orderStat->date_paid ?? null; } }); } public function syncOrderItem($file) : int { $orderItemCount = 0; // Early exit if no valid orders to process if (empty($this->validOrderIds)) { // Helper::rvxLog(0, "No valid orders found, skipping order item sync"); return 0; } // Step 1: Collect valid order items and their IDs $this->validOrdersMetaIds = []; $this->orderItems = []; // Store full order item objects keyed by order_item_id DB::table('woocommerce_order_items')->whereNotIn('order_item_type', ['shipping'])->whereIn('order_id', $this->validOrderIds)->chunk(500, function ($orderItems) { foreach ($orderItems as $orderItem) { $this->validOrdersMetaIds[] = $orderItem->order_item_id; $this->orderItems[$orderItem->order_item_id] = $orderItem; } }); // Early exit if no valid order items found if (empty($this->validOrdersMetaIds)) { // Helper::rvxLog(0, "No valid order items found, skipping meta sync"); return 0; } // Step 2: Fetch associated meta data for the collected order items $this->getOrderItemMeta(); // Step 3: Format and write each order item to the file foreach ($this->orderItems as $orderItemId => $orderItem) { $orderItem->product_id = $this->orderItemProductRelation[$orderItemId] ?? 0; $orderItem->quantity = $this->orderItemQtyRelation[$orderItemId] ?? 0; $orderItem->price = $this->orderItemPriceRelation[$orderItemId] ?? 0.0; $formattedOrderItem = $this->formatOrderItem($orderItem); Helper::appendToJsonl($file, $formattedOrderItem); $orderItemCount++; } Helper::rvxLog($orderItemCount, "Order Item Done"); return $orderItemCount; } public function formatOrderItem($orderItem) : array { $productId = (int) ($orderItem->product_id ?? 0); return ['rid' => 'rid://LineItem/' . (int) $orderItem->order_item_id, 'wp_id' => (int) $orderItem->order_item_id, "wp_unique_id" => Client::getUid() . '-' . (int) $orderItem->order_item_id, 'order_id' => (int) $orderItem->order_id, 'product_wp_unique_id' => Client::getUid() . '-' . $productId, 'name' => $orderItem->order_item_name ?? null, 'quantity' => (int) ($orderItem->quantity ?? 0), 'price' => Helper::formatToTwoDecimalPlaces($orderItem->price ?? 0.0), 'review_id' => null, 'site_id' => Client::getSiteId(), 'fulfillment_status' => $this->orderFullfillmentStatusRelation[(int) $orderItem->order_id] ?? null, 'fulfilled_at' => !empty($this->orderFullfillmentAtRelation[(int) $orderItem->order_id]) ? Helper::validateReturnDate($this->orderFullfillmentAtRelation[(int) $orderItem->order_id]) : null, 'reviewed_at' => null]; } public function getOrderItemMeta() : void { DB::table('woocommerce_order_itemmeta')->whereIn('order_item_id', $this->validOrdersMetaIds)->whereIn('meta_key', ['_product_id', '_qty', '_line_total'])->chunk(100, function ($orderItemMeta) { foreach ($orderItemMeta as $item) { if ($item->meta_key === '_product_id') { $this->orderItemProductRelation[$item->order_item_id] = $item->meta_value; } if ($item->meta_key === '_qty') { $this->orderItemQtyRelation[$item->order_item_id] = $item->meta_value; } if ($item->meta_key === '_line_total') { $this->orderItemPriceRelation[$item->order_item_id] = $item->meta_value; } } }); } } PK �N,]�.K UserServices.phpnu ��� <?php namespace Rvx\Services; use Rvx\Apiz\Http\Response; use Rvx\Utilities\Helper; class UserServices extends \Rvx\Services\Service { /** * @return Response */ public function getUser() { $customer_ids = get_users(array('role' => 'customer', 'fields' => 'ID')); $customer_data_array = array(); foreach ($customer_ids as $customer_id) { $user_data = get_userdata($customer_id); $customer_first_name = \get_user_meta($customer_id, 'first_name', \true); $customer_last_name = \get_user_meta($customer_id, 'last_name', \true); $current_customer_data = array('customer_id' => $customer_id, 'customer_email' => $user_data->user_email, 'customer_username' => $user_data->user_login, 'customer_first_name' => $customer_first_name, 'customer_last_name' => $customer_last_name); $customer_data_array[] = $current_customer_data; } return Helper::rest($customer_data_array)->success("All Customer list"); } } PK �N,]ݤ��� � Api/LoginService.phpnu ��� <?php namespace Rvx\Services\Api; use Rvx\Api\AuthApi; use Rvx\Services\Service; class LoginService extends Service { public function resetPostMeta() { global $wpdb; $insight_key = '_rvx_latest_reviews_insight'; $review_key = '_rvx_latest_reviews'; $post_ids = $wpdb->get_col($wpdb->prepare("SELECT DISTINCT post_id FROM {$wpdb->postmeta} WHERE meta_key = %s OR meta_key = %s", $insight_key, $review_key)); if (!empty($post_ids)) { $post_ids_placeholders = \implode(',', \array_fill(0, \count($post_ids), '%d')); $wpdb->query($wpdb->prepare("DELETE FROM {$wpdb->postmeta} WHERE (meta_key = %s OR meta_key = %s) AND post_id IN ({$post_ids_placeholders})", \array_merge([$insight_key, $review_key], $post_ids))); } } public function resetProductWisePostMeta($product_id) { global $wpdb; $review_key = '_rvx_latest_reviews'; // Validate the product ID if (empty($product_id) || !\is_numeric($product_id)) { return; // Exit if the product ID is invalid } // Prepare and execute the deletion query $wpdb->query($wpdb->prepare("DELETE FROM {$wpdb->postmeta} WHERE meta_key = %s AND post_id = %d", $review_key, $product_id)); } public function forgetPassword($data) { return (new AuthApi())->forgetPassword($data); } public function resetPassword($data) { return (new AuthApi())->resetPassword($data); } } PK �N,]�֙7 �7 ImportExportServices.phpnu ��� <?php namespace Rvx\Services; use Rvx\Api\ReviewImportAndExportApi; use Rvx\CPT\CptHelper; use Rvx\Services\Api\LoginService; use Rvx\Api\AuthApi; use Rvx\Utilities\Helper; use Exception; class ImportExportServices extends \Rvx\Services\Service { private \Rvx\Services\DataSyncService $dataSyncService; private \Rvx\Services\CacheServices $cacheServices; private LoginService $loginService; public function __construct() { $this->dataSyncService = new \Rvx\Services\DataSyncService(); $this->cacheServices = new \Rvx\Services\CacheServices(); $this->loginService = new LoginService(); } public function importSupportedAppStore($data) { return (new ReviewImportAndExportApi())->importSupportedAppStore($data); } public function importStore($request) { $files = $request->get_file_params(); $data = $request->get_params(); // Direct WP DB import $response = $this->importReviewStore($files, $data); global $wpdb; // Initialize tables and reset sync flag (new \Rvx\Handlers\RvxInit\LoadReviewxCreateSiteTable())->init(); \set_transient('rvx_reset_sync_flag', \true, 300); // 5 mins TTL $rvxSites = $wpdb->prefix . 'rvx_sites'; $uid = $wpdb->get_var("SELECT uid FROM {$rvxSites} ORDER BY id DESC LIMIT 1"); if ($uid) { // Mark as not synced initially $wpdb->update($rvxSites, ['is_saas_sync' => 0], ['uid' => $uid], ['%d'], ['%s']); // Start initial sync for all enabled post types $enabled_post_types = (new CptHelper())->usedCPTOnSync('used'); foreach ($enabled_post_types as $post_type) { $this->dataSyncService->dataSync('default', $post_type); } } // Always clean cache and redirect, even if UID or API failed $this->cacheServices->removeCache(); $this->loginService->resetPostMeta(); // Invalidate aggregation transient so next /reviews call fetches fresh data \delete_transient('rvx_admin_aggregation'); return ['status' => 'success', 'message' => 'WordPress import success! Initiating synchronization with ReviewX Cloud...', 'data' => $response]; } public function importReviewStore($files, $data) { // Prevent timeout for large files \set_time_limit(0); $request = $data; $reviews = []; $totalProcessed = 0; $successCount = 0; $file = $files['file']['tmp_name']; $wpReviewIds = []; if (($handle = \fopen($file, 'r')) !== \FALSE) { // Get the header row $header = \fgetcsv($handle); if (!$header) { \fclose($handle); return ['total' => 0, 'success' => 0, 'failed' => 0]; } $map = $request['map'] ?? []; $productIdColumn = $map['product_id'] ?? null; $chunkSize = 50; $currentChunk = []; while (($row = \fgetcsv($handle)) !== \FALSE) { // Combine header with row data if (\count($header) === \count($row)) { $reviewData = \array_combine($header, $row); // Basic validation if ($productIdColumn && !empty($reviewData[$productIdColumn])) { $currentChunk[] = $reviewData; } } // Process chunk if (\count($currentChunk) >= $chunkSize) { $results = $this->processReviewBatch($currentChunk, $request); $totalProcessed += $results['total']; $successCount += $results['success']; $wpReviewIds = \array_merge($wpReviewIds, $results['ids']); $currentChunk = []; // Optional: distinct cleanups if needed per chunk } } // Process remaining if (!empty($currentChunk)) { $results = $this->processReviewBatch($currentChunk, $request); $totalProcessed += $results['total']; $successCount += $results['success']; $wpReviewIds = \array_merge($wpReviewIds, $results['ids']); } \fclose($handle); } $response = ['total' => $totalProcessed, 'success' => $successCount, 'failed' => $totalProcessed - $successCount]; // Log history to SaaS try { (new \Rvx\Api\ReviewImportAndExportApi())->logImportHistory(['name' => \basename($files['file']['name']), 'map' => $request['map'] ?? [], 'wp_review_ids' => $wpReviewIds, 'stats' => ['total_reviews' => $totalProcessed, 'success_reviews' => $successCount, 'failed_reviews' => $totalProcessed - $successCount]]); } catch (\Exception $e) { \error_log("Failed to log import history to SaaS: " . $e->getMessage()); } return $response; } private function processReviewBatch(array $reviews, array $request) : array { $ids = []; $map = $request['map'] ?? []; $productIdColumn = $map['product_id'] ?? null; $total = 0; $success = 0; foreach ($reviews as $reviewData) { $total++; $wpProductId = $productIdColumn && isset($reviewData[$productIdColumn]) ? (int) $reviewData[$productIdColumn] : 0; if (!$wpProductId) { continue; } $postType = $reviewData['Post_Type'] ?? 'product'; try { // We can optimize transient deletion to happen once per product per batch if needed, // but for now keeping it safe. \delete_transient("rvx_{$wpProductId}_latest_reviews"); \delete_transient("rvx_{$wpProductId}_latest_reviews_insight"); $commentId = $this->insertReview($wpProductId, $reviewData, $request, $postType); if ($commentId) { $ids[] = $commentId; $success++; } } catch (Exception $e) { \error_log("Failed to insert review: " . $e->getMessage()); } } return ['total' => $total, 'success' => $success, 'ids' => $ids]; } public function insertReview($reviews_id, $review_data, $request, $post_type) { $mediaArray = []; $map = $request['map'] ?? []; $attachmentKey = $map['attachment'] ?? null; if ($attachmentKey && isset($review_data[$attachmentKey]) && !empty($review_data[$attachmentKey])) { $mediaArray = \explode(',', $review_data[$attachmentKey]); } $comment_type = 'review'; if (!empty($post_type) && \strtolower($post_type) != 'product') { $comment_type = 'comment'; } $customerNameKey = $request['map']['customer_name'] ?? null; $customerEmailKey = $request['map']['customer_email'] ?? null; $feedbackKey = $request['map']['feedback'] ?? null; $createdAtKey = $request['map']['created_at'] ?? null; $comment_data = ['comment_post_ID' => $reviews_id, 'comment_author' => $customerNameKey && isset($review_data[$customerNameKey]) ? $review_data[$customerNameKey] : 'Anonymous', 'comment_author_email' => $customerEmailKey && isset($review_data[$customerEmailKey]) ? $review_data[$customerEmailKey] : '', 'comment_content' => $feedbackKey && isset($review_data[$feedbackKey]) ? $review_data[$feedbackKey] : '', 'comment_date' => $createdAtKey && !empty($review_data[$createdAtKey]) && \strtotime($review_data[$createdAtKey]) !== \false ? \wp_date('Y-m-d H:i:s', \strtotime($review_data[$createdAtKey])) : \wp_date('Y-m-d H:i:s'), 'comment_approved' => Helper::arrayGet($request, 'status'), 'comment_type' => $comment_type]; $comment_id = \wp_insert_comment($comment_data); if ($comment_id && !\is_wp_error($comment_id)) { $titleKey = $request['map']['review_title'] ?? null; $titleValue = $titleKey && isset($review_data[$titleKey]) ? $review_data[$titleKey] : null; \update_comment_meta($comment_id, 'reviewx_title', $titleValue); $ratingColumn = $request['map']['rating'] ?? null; $rating = $ratingColumn && isset($review_data[$ratingColumn]) ? (int) $review_data[$ratingColumn] : 5; if ($rating > 5) { $rating = 5; } elseif ($rating < 1) { $rating = 1; } \update_comment_meta($comment_id, 'rating', $rating); $processedMedia = []; foreach ($mediaArray as $url) { $processedMedia[] = $this->sideloadAttachment(\trim($url)); } \update_comment_meta($comment_id, 'reviewx_attachments', $processedMedia); \update_comment_meta($comment_id, 'verified', Helper::arrayGet($request, 'verified')); \update_comment_meta($comment_id, 'rvx_review_version', 'v2'); // Explicitly trigger aggregation if approved if ($comment_data['comment_approved'] == 1) { \Rvx\CPT\CptAverageRating::update_average_rating($reviews_id); } // Handle Review Reply $replyContentColumn = $request['map']['review_reply'] ?? null; if ($replyContentColumn && isset($review_data[$replyContentColumn]) && !empty($review_data[$replyContentColumn]) && $comment_id) { $repliedAtColumn = $request['map']['replied_at'] ?? null; $repliedAt = $repliedAtColumn && isset($review_data[$repliedAtColumn]) && !empty($review_data[$repliedAtColumn]) ? \strtotime($review_data[$repliedAtColumn]) : \time(); $currentUser = Helper::getWpCurrentUser(); $replyData = ['comment_post_ID' => $reviews_id, 'comment_author' => $currentUser ? $currentUser->display_name : 'Shop Owner', 'comment_author_email' => $currentUser ? $currentUser->user_email : \get_option('admin_email'), 'comment_content' => $review_data[$replyContentColumn], 'comment_type' => 'comment', 'comment_parent' => $comment_id, 'comment_approved' => 1, 'comment_date' => \wp_date('Y-m-d H:i:s', $repliedAt)]; \wp_insert_comment($replyData); } } return $comment_id; } /** * @throws Exception */ public function importRollback($data) { return (new ReviewImportAndExportApi())->importRollback($data); } public function rollbackImportByIds($data) { $wpReviewIds = $data['wp_review_ids'] ?? []; if (empty($wpReviewIds)) { return ['status' => 'error', 'message' => 'Missing wp_review_ids']; } $count = 0; $affectedPosts = []; foreach ($wpReviewIds as $commentId) { $comment = \Rvx\get_comment($commentId); if ($comment) { $affectedPosts[] = $comment->comment_post_ID; if (\Rvx\wp_delete_comment($commentId, \true)) { $count++; } } } // Clear caches for affected products foreach (\array_unique($affectedPosts) as $postId) { (new \Rvx\Services\CacheServices())->removeProductCache($postId); \Rvx\CPT\CptAverageRating::update_average_rating($postId); } (new \Rvx\Services\CacheServices())->removeCache(); return ['status' => 'success', 'message' => "Successfully deleted {$count} reviews", 'deleted_count' => $count]; } /** * @throws Exception */ public function importRestore($data) { return (new ReviewImportAndExportApi())->importRestore($data); } public function exportCsv($data) { return (new ReviewImportAndExportApi())->exportCsv($data); } public function exportHistory() { return (new ReviewImportAndExportApi())->exportHistory(); } public function importHistory() { return (new ReviewImportAndExportApi())->importHistory(); } /** * Sideload an attachment from a URL to the local media library. * * @param string $url * @return string */ protected function sideloadAttachment($url) { if (empty($url) || !\filter_var($url, \FILTER_VALIDATE_URL)) { return $url; } // 1. Same-Domain Check $homeUrl = \Rvx\home_url(); $parsedHome = \parse_url($homeUrl); $parsedUrl = \parse_url($url); if (isset($parsedUrl['host'], $parsedHome['host']) && $parsedUrl['host'] === $parsedHome['host']) { return $url; } // 2. Duplicate Prevention (check by source URL) $args = ['post_type' => 'attachment', 'post_status' => 'inherit', 'meta_query' => [['key' => '_rvx_source_url', 'value' => $url]], 'posts_per_page' => 1, 'fields' => 'ids']; $existing = \Rvx\get_posts($args); if (!empty($existing)) { return \Rvx\wp_get_attachment_url($existing[0]); } // 3. Sideloading Process if (!\function_exists('Rvx\\download_url')) { require_once \ABSPATH . 'wp-admin/includes/file.php'; } if (!\function_exists('Rvx\\media_handle_sideload')) { require_once \ABSPATH . 'wp-admin/includes/media.php'; require_once \ABSPATH . 'wp-admin/includes/image.php'; } $tmp = \Rvx\download_url($url); if (\is_wp_error($tmp)) { \error_log("RVX Import: Failed to download attachment ({$url}): " . $tmp->get_error_message()); return $url; } $file_array = ['name' => \basename($url), 'tmp_name' => $tmp]; // Sideload keeping the file $id = \Rvx\media_handle_sideload($file_array, 0); if (\is_wp_error($id)) { @\unlink($tmp); \error_log("RVX Import: Failed to sideload attachment ({$url}): " . $id->get_error_message()); return $url; } // Store original source URL as metadata \update_post_meta($id, '_rvx_source_url', $url); return \Rvx\wp_get_attachment_url($id); } } PK �N,]�2 2 Service.phpnu ��� <?php namespace Rvx\Services; class Service { } PK �N,]��� � ReviewService.phpnu ��� <?php namespace Rvx\Services; use Exception; use Rvx\Api\ReviewsApi; use Rvx\Enum\ReviewStatusEnum; use Rvx\Utilities\Auth\Client; use Rvx\Utilities\Helper; use Rvx\Utilities\TransactionManager; use Rvx\WPDrill\Response; class ReviewService extends \Rvx\Services\Service { protected ReviewsApi $reviewApi; public function __construct() { $this->reviewApi = new ReviewsApi(); } public function getReviews($data) { $query = \http_build_query($data); return (new ReviewsApi())->getReviews($query); } public function reviewList($data) { $query = \http_build_query($data); return (new ReviewsApi())->reviewList($query); } public function createReview($request) { return TransactionManager::run(function () use($request) { $wpCommentData = $this->prepareWpCommentData($request); $commentId = $this->storeReviewMeta($request, $wpCommentData); return $commentId; }, function ($commentId) use($request) { $appReviewData = $this->prepareAppReviewData($request->get_params(), $commentId); // Ensure SaaS payload rating equals the exact value stored in WP DB $storedRating = \get_comment_meta($commentId, 'rating', \true); $appReviewData['rating'] = \is_numeric($storedRating) ? (float) \round($storedRating, 2) : (float) 0.0; return $this->reviewApi->create($appReviewData); }); } /** * Store review metadata. * * @param array $request The request data. * @param array $wpCommentData The prepared WordPress comment data. * @return int The inserted comment ID. */ public function storeReviewMeta($data, array $wpCommentData) : int { try { $files = $data->get_file_params(); $max_file_size = 100 * 1024 * 1024; $attachments = []; $uploaded_images = isset($files['attachments']) ? $files['attachments'] : []; if ($uploaded_images) { foreach ($uploaded_images['size'] as $size) { if ($size > $max_file_size) { throw new Exception('File size exceeds 100MB limit'); } } $attachments = $this->fileUpload($uploaded_images); } else { $attachments = $data->get_params()['attachments'] ? $data->get_params()['attachments'] : []; } $criterias = isset($data->get_params()['criterias']) ? $data->get_params()['criterias'] : null; $isAllowedMultiCriteria = (new \Rvx\Services\SettingService())->getReviewSettings(get_post_type($data->get_params()['wp_post_id']))['reviews']['multicriteria']['enable'] ?? \false; // Calculate the average rating if ($criterias !== null && $isAllowedMultiCriteria === \true) { $wcAverageRating = $this->calculateAverageRating($criterias); } else { $wcAverageRating = (float) \round($data->get_params()['rating'], 2); } $wpCommentData['comment_meta'] = ['reviewx_title' => \strip_tags(\array_key_exists('title', $data->get_params()) ? $data->get_params()['title'] : null), 'is_recommended' => \array_key_exists('is_recommended', $data->get_params()) && $data->get_params()['is_recommended'] === "true" ? 1 : 0, 'verified' => \array_key_exists('verified', $data->get_params()) && $data->get_params()['verified'], 'is_anonymous' => \array_key_exists('is_anonymous', $data->get_params()) && $data->get_params()['is_anonymous'] === "true" ? 1 : 0, 'rvx_criterias' => $criterias, 'rating' => $wcAverageRating, 'reviewx_attachments' => $attachments, 'rvx_review_version' => 'v2']; $commentId = \wp_insert_comment($wpCommentData); if (!\is_wp_error($commentId) && $commentId > 0) { \Rvx\CPT\CptAverageRating::update_average_rating($wpCommentData['comment_post_ID']); return $commentId; } return 0; } catch (Exception $e) { throw new Exception("Review Save field" . $e->getMessage()); } } public function storeReviewMetaFormWidget($data, array $wpCommentData, $file = []) { try { $attachments = !empty($file) ? $file : []; $criterias = isset($data->get_params()['criterias']) ? $data->get_params()['criterias'] : null; // Calculate the average rating $isAllowedMultiCriteria = (new \Rvx\Services\SettingService())->getReviewSettings(get_post_type($data->get_params()['wp_post_id']))['reviews']['multicriteria']['enable'] ?? \false; if ($criterias !== null && $isAllowedMultiCriteria === \true) { $wcAverageRating = $this->calculateAverageRating($criterias); } else { $wcAverageRating = \round($data->get_params()['rating'], 2); } $wpCommentData['comment_meta'] = ['reviewx_title' => \strip_tags(\array_key_exists('title', $data->get_params()) ? $data->get_params()['title'] : null), 'is_recommended' => \array_key_exists('is_recommended', $data->get_params()) && $data->get_params()['is_recommended'] === "true" ? 1 : 0, 'verified' => \array_key_exists('verified', $data->get_params()) && $data->get_params()['verified'], 'is_anonymous' => \array_key_exists('is_anonymous', $data->get_params()) && $data->get_params()['is_anonymous'] === "true" ? 1 : 0, 'rvx_criterias' => $criterias, 'rating' => $wcAverageRating, 'reviewx_attachments' => $attachments, 'rvx_review_version' => 'v2']; $commentId = \wp_insert_comment($wpCommentData); if (!\is_wp_error($commentId) && $commentId > 0) { \Rvx\CPT\CptAverageRating::update_average_rating($wpCommentData['comment_post_ID']); return (int) $commentId; } return 0; } catch (Exception $e) { \error_log($e->getMessage()); } } public function prepareWpCommentData($request) : array { $data = (array) (new \Rvx\Services\SettingService())->getReviewSettings(get_post_type($request['wp_post_id'])); $review_type = 'review'; if (get_post_type($request['wp_post_id']) !== 'product') { $review_type = 'comment'; } $status = Helper::arrayGet($request->get_params(), 'status') ?? $data['reviews']['auto_approve_reviews']; if (!$status || $status === 'false' || $status === '0') { $status = 0; } else { $status = 1; } return ['comment_post_ID' => absint($request['wp_post_id']), 'comment_content' => \strip_tags(\trim($request['feedback'], '"') ?? null), 'comment_author' => sanitize_text_field($request['reviewer_name']), 'comment_author_email' => sanitize_text_field($request['reviewer_email']), 'comment_type' => $review_type, 'comment_approved' => $status, 'comment_agent' => $_SERVER['HTTP_USER_AGENT'], 'comment_author_IP' => $_SERVER['REMOTE_ADDR'], 'comment_date_gmt' => current_time('mysql', 1), 'user_id' => sanitize_text_field($request['user_id']) ?? 0, 'comment_date' => current_time('mysql', \true)]; } /** * Prepare application review data. * * @param array $request The request data. * @param int $commentId The comment ID. * @return array The prepared application review data. */ public function prepareAppReviewData(array $payloadData, int $commentId) : array { $data = ['wp_id' => $commentId, 'product_wp_unique_id' => Client::getUid() . '-' . $payloadData['wp_post_id'] ?? null, 'wp_post_id' => $payloadData['wp_post_id'] ?? null, 'reviewer_email' => sanitize_text_field($payloadData['reviewer_email'] ?? null), 'reviewer_name' => sanitize_text_field($payloadData['reviewer_name'] ?? null), 'rating' => (float) sanitize_text_field(\round($payloadData['rating'], 2) ?? 0.0), 'feedback' => \strip_tags($payloadData['feedback'] ?? null), 'is_verified' => Helper::arrayGet($payloadData, 'verified'), 'auto_publish' => Helper::arrayGet($payloadData, 'status'), 'created_at' => current_time('mysql', \true), 'title' => \strip_tags($payloadData['title'] ?? null), 'attachments' => isset($payloadData['attachments']) ? $payloadData['attachments'] : []]; $data = \array_merge($data, $payloadData); $criterias = Helper::arrayGet($data, 'criterias'); if ($criterias) { $data['criterias'] = \array_map('intval', $criterias); } return $data; } public function aiReviewCount() { return $this->reviewApi->aiReviewCount(); } public function fileUpload($uploaded_images) { if (!isset($uploaded_images['name'])) { return; } $uploaded_urls = []; foreach ($uploaded_images['name'] as $key => $image_name) { $file = ['name' => $uploaded_images['name'][$key], 'type' => $uploaded_images['type'][$key], 'tmp_name' => $uploaded_images['tmp_name'][$key], 'error' => $uploaded_images['error'][$key], 'size' => $uploaded_images['size'][$key]]; $upload = wp_handle_upload($file, ['test_form' => \false]); if (isset($upload['url'])) { $attachment_id = wp_insert_attachment(['guid' => $upload['url'], 'post_mime_type' => $upload['type'], 'post_title' => sanitize_file_name($file['name']), 'post_content' => '', 'post_status' => 'publish'], $upload['file']); $attachment_data = wp_generate_attachment_metadata($attachment_id, $upload['file']); wp_update_attachment_metadata($attachment_id, $attachment_data); $uploaded_urls[] = wp_get_attachment_url($attachment_id); } } return $uploaded_urls; } public function deleteReview($request) { $wpUniqueId = sanitize_text_field($request->get_param('wpUniqueId')); $this->reviewDelete($request->get_params()); $delete_rev = (new ReviewsApi())->deleteReviewData($wpUniqueId); if ($delete_rev) { return Helper::rest($delete_rev)->success(); } return Helper::rest(null)->fails(__("Review Delete Fails", "reviewx")); } public function reviewDelete($data) { $parts = \explode('-', $data['wpUniqueId']); $last_part = \end($parts); wp_delete_comment($last_part, \true); } public function restoreReview($request) { $wpUniqueId = sanitize_text_field($request->get_param('wpUniqueId')); return TransactionManager::run(function () use($wpUniqueId) { $this->restoreTrashToPublish($wpUniqueId); return \true; }, function () use($wpUniqueId) { return $this->reviewApi->restoreReview($wpUniqueId); }); } public function restoreTrashToPublish($review_unique_id) { $id = $this->getLastSegment($review_unique_id); $status = \get_comment_meta($id, '_wp_trash_meta_status', \true); if ($status) { wp_set_comment_status($id, 'approve'); } else { wp_set_comment_status($id, 'hold'); } } public function getReview($request) { $wpUniqueId = sanitize_text_field($request->get_param('wpUniqueId')); return (new ReviewsApi())->getReview($wpUniqueId); } public function restoreTrashItem($data) { //Bulk trash restore $response = (new ReviewsApi())->restoreTrashItem($data); if ((int) $response->getStatusCode() === 200) { $this->bulkRestoreTrashItem($data); } return $response; } public function bulkRestoreTrashItem($data) { foreach ($data['wp_id'] as $id) { $status = \get_comment_meta($id, '_wp_trash_meta_status', \true); if ($status == 'approve' || $status == ReviewStatusEnum::APPROVED) { wp_set_comment_status($id, 'approve'); } elseif ($status == 'hold' || $status == ReviewStatusEnum::PENDING) { wp_set_comment_status($id, 'hold'); } elseif ($status == 'unapproved' || $status == ReviewStatusEnum::UNPUBLISHED) { wp_set_comment_status($id, 'hold'); } elseif ($status == 'spam' || $status == ReviewStatusEnum::SPAM) { wp_set_comment_status($id, 'spam'); } else { wp_set_comment_status($id, 'approve'); } } } public function isVerify($request) { $wpUniqueId = $request['wpUniqueId']; $status = $request->get_param('status'); $verifyData = (new ReviewsApi())->verifyReview($status, $wpUniqueId); if ($verifyData) { return Helper::rest($verifyData()->from('data')->toArray())->success(); } return Helper::rest(null)->fails(__('Verify Fail', 'reviewx')); } public function isvisibility($request) { return TransactionManager::run(function () use($request) { return $this->visibilitySpam($request->get_params()); }, function () use($request) { $wpUniqueId = $request['wpUniqueId']; $statusData = ['status' => $request->get_param('status')]; return $this->reviewApi->visibilityReviewData($statusData, $wpUniqueId); }); } public function visibilitySpam($data) { if (ReviewStatusEnum::SPAM === $data['status']) { wp_spam_comment($data['wp_id']); } if (ReviewStatusEnum::APPROVED === $data['status']) { wp_set_comment_status($data['wp_id'], 'approve'); } if (ReviewStatusEnum::PENDING === $data['status']) { wp_set_comment_status($data['wp_id'], 'hold'); } if (ReviewStatusEnum::TRASH === $data['status']) { wp_trash_comment($data['wp_id']); } return \true; } public function updateReqEmail($request) { $data = []; $wpUniqueId = $request['wpUniqueId']; $verifyData = (new ReviewsApi())->sendUpdateReviewRequestEmail($data, $wpUniqueId); if ($verifyData) { return Helper::rest($verifyData()->from('data')->toArray())->success(__("Verify", "reviewx")); } return Helper::rest(null)->fails(__('Fail', 'reviewx')); } public function reviewReplies($request) { $wpUniqueId = $request['wpUniqueId']; $replies = ['reply' => $request['reply'], 'wp_id' => $this->getLastSegment($wpUniqueId)]; $commentReply = (new ReviewsApi())->commentReply($replies, $wpUniqueId); if ($commentReply) { $this->reviewRepliesForWp($replies); $this->reviewCacheDelete($this->getLastSegment($wpUniqueId)); return Helper::rvxApi(['success' => null])->success('Reply submitted sucesfully.', 200); } return Helper::rest(null)->fails(__('Replies Fail', 'reviewx')); } public function reviewCacheDelete($review_id) { $post_id = get_comment($review_id)->comment_post_ID ?? null; \delete_transient("rvx_{$post_id}_latest_reviews"); \delete_transient("rvx_{$post_id}_latest_reviews_insight"); } public function reviewRepliesForWp($replies) { $parentReviewId = $replies['wp_id']; $parent_comment = get_comment($parentReviewId); if (!$parent_comment) { return \false; } $replyData = $this->prepareDataForReply($parent_comment, $replies, $parentReviewId); if ($parent_comment->comment_parent == 0) { $replayId = \wp_insert_comment($replyData); return \true; } } public function prepareDataForReply($parent_comment, $replies, $parentReviewId) { return ['comment_post_ID' => $parent_comment->comment_post_ID, 'comment_author' => $parent_comment->comment_author, 'comment_author_email' => $parent_comment->comment_author_email, 'comment_author_url' => '', 'comment_content' => $replies['reply'], 'comment_type' => $parent_comment->comment_type, 'comment_parent' => $parentReviewId, 'user_id' => get_current_user_id(), 'comment_approved' => 1, 'comment_date' => current_time('mysql'), 'comment_date_gmt' => current_time('mysql', 1)]; } private function getLastSegment($string) { $lastHyphenPos = \strrpos($string, '-'); if ($lastHyphenPos === \false) { return $string; } return \substr($string, $lastHyphenPos + 1); } public function reviewRepliesUpdate($request) { $wpUniqueId = $request['wpUniqueId']; $repliesUpdate = ['reply' => $request['reply']]; $commentReply = (new ReviewsApi())->updateCommentReply($repliesUpdate, $wpUniqueId); if ($commentReply) { $this->reviewRepliesUpdateForWp($wpUniqueId, $repliesUpdate); $this->reviewCacheDelete($wpUniqueId); return Helper::rest($commentReply()->from('data')->toArray())->success(); } return Helper::rest(null)->fails(__('Update Fail', 'reviewx')); } private function reviewRepliesUpdateForWp($wpUniqueId, $repliesUpdate) { $parentReviewId = $this->getLastSegment($wpUniqueId); $comment_data = array('comment_ID' => $parentReviewId, 'comment_content' => $repliesUpdate['reply'], 'comment_date' => current_time('mysql'), 'comment_date_gmt' => current_time('mysql', 1)); wp_update_comment($comment_data); } public function reviewRepliesDelete($request) { $wpUniqueId = $request['wpUniqueId']; $commentReply = (new ReviewsApi())->deleteCommentReply($wpUniqueId); if ($commentReply) { return Helper::rest($commentReply()->from('data')->toArray())->success(); } return Helper::rest(null)->fails(__('Delete Fail', 'reviewx')); } public function aiReview($request) { global $wpdb; $wpdb->query('START TRANSACTION'); try { $comment_id = \wp_insert_comment($this->aiReviewWp($request)); $reviApp = $this->aiReviewApp($request, $comment_id); $reviewApi = new ReviewsApi(); $res = $reviewApi->aiReview($reviApp); $resReviewData = $res->getApiData(); wp_update_comment($comment_id, $resReviewData); $wpdb->query('COMMIT'); return $res; } catch (Exception $e) { $wpdb->query('ROLLBACK'); } } public function aggregationMeta($request) { try { foreach ($request->get_params() as $data) { $productId = Helper::arrayGet($data, 'product_wp_id'); if (!$productId) { continue; } $aggregation_data = \json_encode(wp_slash(Helper::arrayGet($data, "meta")), \JSON_UNESCAPED_UNICODE | \JSON_UNESCAPED_SLASHES); \set_transient("rvx_{$productId}_latest_reviews_insight", $aggregation_data, 604800); // Expires in 7 days } return Helper::rest()->success("Success"); } catch (Exception $e) { return Helper::rest($e->getMessage())->fails("Fails"); } } public function aiReviewApp($request, $comment_id) { return ["wp_id" => $comment_id, "product_wp_unique_id" => $request['product_wp_unique_id'], "wp_post_id" => $request['wp_post_id'], "max_reviews" => $request['max_reviews'], "status" => $request['status'], "verified" => $request['verified'], "region" => $request['region'], "religious" => $request['religious'], "gender" => $request['gender']]; } public function aiReviewWp($request) { $review_type = 'review'; if (get_post_type($request['wp_post_id']) !== 'product') { $review_type = 'comment'; } return ['comment_post_ID' => absint($request['product_id']), 'comment_content' => sanitize_text_field($request['feedback'] ?? ''), 'comment_author' => sanitize_text_field(get_userdata(get_current_user_id())->display_name), 'comment_author_email' => sanitize_text_field(get_userdata(get_current_user_id())->user_email), 'comment_type' => $review_type, 'comment_approved' => sanitize_text_field($request['status'] ?? ''), 'comment_agent' => $_SERVER['HTTP_USER_AGENT'], 'comment_author_IP' => $_SERVER['REMOTE_ADDR'], 'comment_date_gmt' => current_time('mysql', 1), 'comment_date' => current_time('mysql', \true)]; } public function updateWooReview($updatedData, $wpUpdatedData) { $wpUniqueId = $updatedData['wp_unique_id']; try { $reviewApi = new ReviewsApi(); $res = $reviewApi->updateWooReviewData($updatedData, $wpUniqueId); if ($res->getStatusCode() !== Response::HTTP_OK) { return ['error' => $res->getStatusCode()]; } return $res; } catch (Exception $e) { return ["error" => "Review Not updated"]; } } public function updateReview($request) { $reviewId = $request->get_param('wp_id'); $wpUniqueId = $request->get_param('wpUniqueId'); return TransactionManager::run(function () use($request, $reviewId) { $existingReview = get_comment($reviewId); if (!$existingReview) { return \false; } $wpCommentData = $this->prepareUpdateWpComment($request, $existingReview); wp_update_comment(['comment_ID' => $reviewId, 'comment_content' => \strip_tags($wpCommentData['comment_content']), 'comment_approved' => sanitize_text_field($wpCommentData['comment_approved']), 'comment_author_email' => sanitize_text_field($wpCommentData['comment_author_email']), 'comment_author' => sanitize_text_field($wpCommentData['comment_author'])]); $this->reviewCacheDelete($reviewId); $this->updateReviewMeta($reviewId, $request); // Update average rating for the post \Rvx\CPT\CptAverageRating::update_average_rating($existingReview->comment_post_ID); return \true; }, function () use($request, $wpUniqueId) { $appReviewData = $this->prepareUpdateAppReview($request->get_params(), $request->get_file_params()); $response = $this->reviewApi->updateReviewData($appReviewData, $wpUniqueId); if (!\is_object($response)) { return \false; } return $response; }); } public function updateReviewMeta($reviewId, $data) { $params = $data->get_params(); // 1. Title if (isset($params['title'])) { \update_comment_meta($reviewId, 'reviewx_title', sanitize_text_field($params['title'])); } // 2. Rating & Criterias $criterias = $params['criterias'] ?? null; if ($criterias !== null) { \update_comment_meta($reviewId, 'rvx_criterias', $criterias); $wp_post_id = $params['wp_post_id'] ?? get_comment($reviewId)->comment_post_ID; $isAllowedMultiCriteria = (new \Rvx\Services\SettingService())->getReviewSettings(get_post_type($wp_post_id))['reviews']['multicriteria']['enable'] ?? \false; if ($isAllowedMultiCriteria) { $wcAverageRating = $this->calculateAverageRating($criterias); \update_comment_meta($reviewId, 'rating', $wcAverageRating); \update_comment_meta($reviewId, 'reviewx_rating', $wcAverageRating); } } elseif (isset($params['rating'])) { $rating = (float) \round($params['rating'], 2); \update_comment_meta($reviewId, 'rating', $rating); \update_comment_meta($reviewId, 'reviewx_rating', $rating); } // 3. Flags (Safe updates using filter_var for string booleans) if (isset($params['verified'])) { \update_comment_meta($reviewId, 'verified', \filter_var($params['verified'], \FILTER_VALIDATE_BOOLEAN)); } if (isset($params['is_recommended'])) { \update_comment_meta($reviewId, 'is_recommended', \filter_var($params['is_recommended'], \FILTER_VALIDATE_BOOLEAN) ? 1 : 0); } if (isset($params['is_anonymous'])) { \update_comment_meta($reviewId, 'is_anonymous', \filter_var($params['is_anonymous'], \FILTER_VALIDATE_BOOLEAN) ? 1 : 0); } // 4. Attachments if (isset($params['attachments'])) { \update_comment_meta($reviewId, 'reviewx_attachments', $params['attachments'] ?? []); } } /** * Prepare WordPress comment data for updating. * * @param array $request The request data. * @param WP_Comment $existingReview The existing review data. * @return array The prepared WordPress comment data. */ public function prepareUpdateWpComment($request, $existingReview) { return ['comment_content' => \strip_tags($request['feedback'] ?? $existingReview->comment_content), 'comment_approved' => sanitize_text_field($request['status'] ?? $existingReview->comment_approved), 'comment_author_email' => sanitize_text_field($request['reviewer_email'] ?? $existingReview->comment_author_email), 'comment_author' => sanitize_text_field($request['reviewer_name'] ?? $existingReview->comment_author)]; } public function prepareUpdateAppReview(array $payloadData, array $files) { $isRecommended = isset($payloadData['is_recommended']) == 1 ? \true : \false; // $uploaded_images = $files['attachments']; // $uploaded_images = isset($files['attachments']) ? $files['attachments'] : []; // $attachments = $this->fileUpload($uploaded_images); $criterias = Helper::arrayGet($payloadData, 'criterias'); $rating = Helper::arrayGet($payloadData, 'rating', null); if ($criterias) { $wp_post_id = $payloadData['wp_post_id'] ?? null; $isAllowedMultiCriteria = $wp_post_id ? (new \Rvx\Services\SettingService())->getReviewSettings(get_post_type($wp_post_id))['reviews']['multicriteria']['enable'] ?? \false : \true; if ($isAllowedMultiCriteria) { $rating = $this->calculateAverageRating($criterias); } } $payloadData['rating'] = $rating ? (float) \round($rating, 2) : (float) 0.0; $data = [ // 'rating' => (int)$payloadData['rating'], 'feedback' => \strip_tags($payloadData['feedback']), 'title' => \strip_tags($payloadData['title']), 'reviewer_name' => sanitize_text_field($payloadData['reviewer_name']), 'reviewer_email' => sanitize_text_field($payloadData['reviewer_email']), 'date' => current_time('mysql', \true), 'anonymous' => isset($payloadData['anonymous']), 'is_recommended' => $isRecommended, 'attachment_access' => isset($payloadData['attachment_access']), ]; $data = \array_merge($payloadData, $data); $criterias = Helper::arrayGet($payloadData, 'criterias'); if ($criterias) { $payloadData['criterias'] = \array_map('intval', $criterias); } return $data; } public function getWidgetReviewsForProduct($request) { return (new ReviewsApi())->getWidgetReviewsForProductApi($request); } public function getWidgetAllReviewsForSite($request, $site_id) { return (new ReviewsApi())->getWidgetAllReviewsForSiteApi($request, $site_id); } public function getWidgetReviewsListShortcode($request) { return (new ReviewsApi())->getWidgetReviewsListShortcodeApi($request); } public function settingMeta($request) { try { $settings = $request->get_params()['meta']; (new \Rvx\Services\SettingService())->updateSettingsData($settings); return Helper::rest()->success("Success"); } catch (Exception $th) { return Helper::rest()->fails("Fails"); } } public static function getSpecificReviewItem($data) { $review_ids = $data['review_ids']; $uid = Client::getUid(); $review_wp_unique_ids = \array_map(function ($id) use($uid) { return $uid . '-' . $id; }, $review_ids); $data = []; $data['review_wp_unique_ids'] = $review_wp_unique_ids; return (new ReviewsApi())->getSpecificReviewItem($uid, $data); } public function getSingleProductAllReviews($data) { return (new ReviewsApi())->getSingleProductAllReviews($data); } public function getWidgetInsight($request) { return (new ReviewsApi())->getWidgetInsight($request); } public function reviewBulkUpdate($data) { return TransactionManager::run(function () use($data) { $this->reviewBulkStatusUpdateForWp($data); return \true; }, function () use($data) { return $this->reviewApi->reviewBulkUpdate($data); }); } public function reviewBulkStatusUpdateForWp($data) { if (ReviewStatusEnum::SPAM === $data['status']) { foreach ($data['wp_id'] as $id) { wp_spam_comment($id); } } if (ReviewStatusEnum::APPROVED === $data['status']) { foreach ($data['wp_id'] as $id) { wp_set_comment_status($id, 'approve'); } } if (ReviewStatusEnum::PENDING === $data['status']) { foreach ($data['wp_id'] as $id) { wp_set_comment_status($id, 'hold'); } } if (ReviewStatusEnum::TRASH === $data['status']) { foreach ($data['wp_id'] as $id) { wp_trash_comment($id); } } } public function reviewBulkTrash($data) { return TransactionManager::run(function () use($data) { $this->bulkTrashInWp($data); return \true; }, function () use($data) { return $this->reviewApi->reviewBulkTrash($data); }); } public function bulkTrashInWp($data) { if (!\is_array($data)) { return \false; } foreach ($data['wp_id'] as $review_id) { wp_trash_comment($review_id, \true); } } public function reviewEmptyTrash($data) { return TransactionManager::run(function () use($data) { $this->emptyTrashInWp($data); return \true; }, function () use($data) { return $this->reviewApi->reviewEmptyTrash(); }); } public function emptyTrashInWp($review_ids) { if (!\is_array($review_ids)) { return \false; } foreach ($review_ids['wp_ids'] as $review_id) { wp_delete_comment($review_id, \true); } return \true; } public function reviewAggregation() { return (new ReviewsApi())->reviewAggregation(); } public function saveWidgetReviewsForProduct($request) { return TransactionManager::run(function () use($request) { return $this->dataMerge($request); }, function ($data) { return $this->reviewApi->saveWidgetReviewsForProductApi($data); }); } public function dataMerge($request) { $files = $request->get_file_params(); $max_file_size = 100 * 1024 * 1024; // 100MB in bytes $attachments = []; if (!empty($files['attachments']['name']) && \is_array($files['attachments']['size'])) { foreach ($files['attachments']['size'] as $size) { if ($size > $max_file_size) { return ['error' => 'File size exceeds 100MB limit']; } } $attachments = $this->fileUpload($files['attachments']); } $wpCommentData = $this->prepareWpCommentData($request); $commentId = $this->storeReviewMetaFormWidget($request, $wpCommentData, $attachments); $productId = $request->get_param('wp_post_id'); $siteUid = Client::getUid(); $productWpUniqueId = $siteUid . '-' . $productId; $data = \array_merge($request->get_params(), ["wp_id" => $commentId, "site_uid" => $siteUid, "product_wp_unique_id" => $productWpUniqueId, "is_anonymous" => $request['is_anonymous'] == "true" ? \true : \false, "is_verified" => $request['verified'] == "true" ? \true : \false, "is_customer_verified" => $request['is_customer_verified'] == "true" ? \true : \false, "attachments" => $attachments, 'created_at' => current_time('mysql', \true), 'is_recommended' => $request['is_recommended'] == "true" ? \true : \false]); $data['feedback'] = \strip_tags($request['feedback']); $data['title'] = \strip_tags($request['title']); $criterias = Helper::arrayGet($data, 'criterias'); $post_type = get_post_type($productId); $review_setting = (new \Rvx\Services\SettingService())->getReviewSettings($post_type); $criteria_enabled = $review_setting['reviews']['multicriteria']['enable']; if ($criterias && $criteria_enabled === \true) { $data['criterias'] = \array_map('intval', $criterias); // Rating Modified $total_rating = \array_sum($data['criterias']); $rating_count = \count($data['criterias']); // Count of valid values $data['rating'] = $rating_count > 0 ? (float) \round($total_rating / $rating_count, 2) : (float) 0.0; } else { $data['rating'] = $request['rating'] ? (float) \round($request['rating'], 2) : (float) 0.0; } return $data; } public function requestReviewEmailAttachment($request) { // Get parameters and file payload $wpUniqueId = $request->get_params(); $payload = $request->get_file_params(); // Initialize the response array $response = []; // Maximum file size in bytes (e.g., 5MB) $maxFileSize = 5 * 1024 * 1024; // 5 MB // Allowed mime types for images $allowedMimeTypes = ['image/jpeg', 'image/png', 'image/gif', 'video/mp4', 'video/webm', 'video/ogg']; // Loop through the reviews array foreach ($wpUniqueId['reviews'] as $index => $reviewData) { if (isset($reviewData['wp_unique_id'])) { $wp_unique_id = $this->getLastSegment($reviewData['wp_unique_id']); // Prepare the files array for this review $files = []; if (isset($payload['reviews']['tmp_name'][$index]['files'])) { foreach ($payload['reviews']['tmp_name'][$index]['files'] as $fileIndex => $tmpFile) { // Get the corresponding file details $file_info = ['name' => $payload['reviews']['name'][$index]['files'][$fileIndex]['file'], 'tmp_name' => $tmpFile['file'], 'type' => $payload['reviews']['type'][$index]['files'][$fileIndex]['file'], 'error' => $payload['reviews']['error'][$index]['files'][$fileIndex]['file'], 'size' => $payload['reviews']['size'][$index]['files'][$fileIndex]['file']]; // Validate file size if ($file_info['size'] > $maxFileSize) { continue; } // Validate file type (mime type) if (!\in_array($file_info['type'], $allowedMimeTypes)) { continue; } if ($file_info['error'] === \UPLOAD_ERR_OK) { // Upload the file to WordPress $upload = wp_handle_upload($file_info, ['test_form' => \false]); if (!isset($upload['error']) && isset($upload['url'])) { // Add the file URL to the files array $files[] = ['file' => $upload['url']]; } } } } $image_urls = \array_map(function ($file) { return $file['file']; }, $files); \update_comment_meta($wp_unique_id, 'reviewx_attachments', $image_urls); // Add the data for this review $response[] = ['wp_unique_id' => Client::getUid() . '-' . $wp_unique_id, 'files' => $files]; } } return $response; } public function reviewMoveToTrash($data) { return TransactionManager::run(function () use($data) { $this->trashInWp($data); return \true; }, function () use($data) { return $this->reviewApi->reviewMoveToTrash($data); }); } public function trashInWp($data) { $parts = \explode('-', $data['WpUniqueId']); $last_part = \end($parts); wp_trash_comment($last_part); } public function likeDIslikePreference($data) { return (new ReviewsApi())->likeDIslikePreference($data); } public function reviewListMultiCriteria() { return (new ReviewsApi())->reviewListMultiCriteria(); } public function highlight($data) { return (new ReviewsApi())->highlight($data); } public function bulkTenReviews($data) { return $data; } public function reviewRequestStoreItem($data) { global $wpdb; $wpdb->query('START TRANSACTION'); try { $comment_ids = $this->commentInsertableFormEmail($data); $saasData = $this->prepareAppReviewDataFormEmail($comment_ids); // return $this->reviewApi->reviewRequestStoreItem($saasData, $uid); $wpdb->query('COMMIT'); return $saasData; } catch (Exception $e) { $wpdb->query('ROLLBACK'); } } public function commentInsertableFormEmail($data) { $wpCommentData = $this->prepareWpCommentDataForEmail($data); return $this->storeReviewMetaForEmail($data, $wpCommentData); } public function prepareAppReviewDataFormEmail(array $commentIds) : array { $reviewData = ['reviews' => []]; foreach ($commentIds as $commentId) { $comment = get_comment($commentId); if (!$comment) { continue; } $data = ['wp_id' => (int) $comment->comment_ID, 'product_wp_unique_id' => Client::getUid() . '-' . $comment->comment_post_ID, 'wp_post_id' => (int) $comment->comment_post_ID, 'reviewer_email' => $comment->comment_author_email, 'reviewer_name' => $comment->comment_author, 'rating' => (float) \round(\get_comment_meta($comment->comment_ID, 'rating', \true), 2), 'feedback' => $comment->comment_content, 'created_at' => $comment->comment_date, 'title' => \get_comment_meta($comment->comment_ID, 'rvx_comment_title', \true), 'order_item_wp_unique_id' => \get_comment_meta($comment->comment_ID, 'rvx_comment_order_item', \true), 'criterias' => \get_comment_meta($comment->comment_ID, 'rvx_criterias', \true)]; $reviewData['reviews'][] = $data; } return $reviewData; } public function prepareWpCommentDataForEmail($data) : array { //Send email review only product $settingsData = (new \Rvx\Services\SettingService())->getReviewSettings('product'); $auto_approve_reviews = $settingsData['reviews']['auto_approve_reviews']; $review_type = 'review'; if (get_post_type($request['wp_post_id']) !== 'product') { $review_type = 'comment'; } $dataStore = []; foreach ($data['reviews'] as $review) { $dataWp = ['comment_post_ID' => absint($review['product_wp_id']), 'comment_content' => sanitize_text_field($review['feedback'] ?? ''), 'comment_author' => sanitize_text_field($review['reviewer_name']), 'comment_author_email' => sanitize_text_field($review['reviewer_email']), 'comment_type' => $review_type, 'comment_approved' => $auto_approve_reviews === \true ? 1 : 0, 'comment_agent' => $_SERVER['HTTP_USER_AGENT'], 'comment_author_IP' => $_SERVER['REMOTE_ADDR'], 'comment_date_gmt' => current_time('mysql', 1), 'user_id' => absint($review['user_id']) ?? 0, 'comment_date' => current_time('mysql', \true)]; $dataStore[] = $dataWp; } return $dataStore; } public function storeReviewMetaForEmail($data, array $wpCommentData) : array { try { $id = []; $isAllowedMultiCriteria = (new \Rvx\Services\SettingService())->getReviewSettings('product')['reviews']['multicriteria']['enable'] ?? \false; foreach ($wpCommentData as $index => $comment) { $criterias = $data['reviews'][$index]['criterias'] ?? null; if (!empty($criterias) && $criterias !== null && $isAllowedMultiCriteria === \true) { // Calculate the average rating $wcAverageRating = $this->calculateAverageRating($criterias); $modified_criteria = \json_encode($criterias); } else { $wcAverageRating = (float) sanitize_text_field(\round($data['reviews'][$index]['rating'], 2)) ?? 0.0; $modified_criteria = null; } $commentId = \wp_insert_comment($comment); add_comment_meta($commentId, 'rvx_comment_title', sanitize_text_field($data['reviews'][$index]['title'] ?? null)); \update_comment_meta($commentId, 'rvx_criterias', $modified_criteria); add_comment_meta($commentId, 'rating', $wcAverageRating); add_comment_meta($commentId, 'rvx_comment_order_item', sanitize_text_field($data['reviews'][$index]['order_item_wp_unique_id'])); add_comment_meta($commentId, 'verified', 1); add_comment_meta($commentId, 'is_recommended', 1); add_comment_meta($commentId, 'reviewx_attachments', []); add_comment_meta($commentId, 'rvx_review_version', 'v2'); $id[] = $commentId; } return $id; } catch (Exception $e) { throw new Exception('Something went wrong!' . $e->getMessage()); } } /** * Calculate the average rating from criteria values. * * @param array|null $criterias An array containing numeric values or key-value pairs with numeric values. * @return int The ceiling value of the average, or 1 if no valid data exists. */ public function calculateAverageRating($criterias) { // Ensure $criterias is an array; default to an empty array if it's null or not an array if (!\is_array($criterias)) { return (float) 0.0; // Fallback value } // Normalize all values to float $values = \array_map(function ($value) { return \is_numeric($value) ? (int) $value : 0; // Convert numeric values to float, default to 0.00 }, \array_values($criterias)); // Filter out any invalid (zero or negative) values $values = \array_filter($values, function ($value) { return $value > 0; // Keep only positive integers }); $total = \array_sum($values); // Sum of all valid values $count = \count($values); // Count of valid values return $count > 0 ? (float) \round($total / $count, 2) : (float) 0.0; // Calculate the average and round up } public function thanksMessage($request) { return ['message' => "Thank you for sharing your review"]; } public function setAllReviewsMetaTransient($site_id, $post_type, $latest_reviews) { $post_type = $post_type != null ? $post_type : 'all'; \set_transient("rvx_{$site_id}_{$post_type}_reviews", wp_slash($latest_reviews), 3600); // Expires in 1 hour } public function postMetaReviewInsert($id, $latest_reviews) { \set_transient("rvx_{$id}_latest_reviews", wp_slash($latest_reviews), 3600); // Expires in 1 hour } public function allReviewApproveCount() : int { global $wpdb; $query = $wpdb->prepare("SELECT COUNT(*) \n FROM {$wpdb->comments} \n WHERE comment_approved = '1' \n AND comment_parent = 0 \n AND comment_type IN ('review','comment')"); return (int) $wpdb->get_var($query); } public function allReviewPendingCount() : int { global $wpdb; $query = $wpdb->prepare("SELECT COUNT(*) \n FROM {$wpdb->comments} \n WHERE comment_approved = '0' \n AND comment_parent = 0\n AND comment_type IN ('review','comment')"); return (int) $wpdb->get_var($query); } public function saasStatusReviewCount() { $data = \get_transient('rvx_reviews_data_list'); if (\is_array($data)) { return $data['count']; } return []; } public function makeSaaSCallDecision() { $approveReviewCount = $this->allReviewApproveCount(); $pendingReviewCount = $this->allReviewPendingCount(); $saasApproveReviewCount = \array_key_exists('published', $this->saasStatusReviewCount()) ? $this->saasStatusReviewCount()['published'] : 0; $saasPendingReviewCount = \array_key_exists('pending', $this->saasStatusReviewCount()) ? $this->saasStatusReviewCount()['pending'] : 0; if ($approveReviewCount != $saasApproveReviewCount) { return \true; } if ($saasPendingReviewCount != $pendingReviewCount) { return \true; } return \false; } } PK �N,]$��D D EmailService.phpnu ��� <?php namespace Rvx\Services; use Rvx\Apiz\Http\Response; use Rvx\Api\EmailApi; class EmailService extends \Rvx\Services\Service { /** * @return Response */ public function index() { return (new EmailApi())->index(); } /** * @return Response */ public function store($data) { return (new EmailApi())->create($data); } /** * @return Response */ public function update($data) { return (new EmailApi())->update($data); } /** * @return Response */ public function remove($request) { return (new EmailApi())->remove(); } public function mailRequest($data) { $query = \http_build_query($data); return (new EmailApi())->mailRequest($query); } public function content() { return (new EmailApi())->content(); } public function saveEmailRequest($data) { return (new EmailApi())->saveEmailRequest($data); } public function followup($data) { return (new EmailApi())->followup($data); } public function photoReview($data) { return (new EmailApi())->photoReview($data); } public function testMail($data) { return (new EmailApi())->testMail($data); } public function markAsComplete($data) { return (new EmailApi())->markAsComplete($data['uid']); } public function emailCancel($data) { return (new EmailApi())->emailCancel($data['uid']); } public function requestEmailSend($data) { return (new EmailApi())->requestEmailSend($data); } public function requestEmailResend($data) { return (new EmailApi())->requestEmailResend($data); } public function requestEmailUnsubscribe() { return (new EmailApi())->requestEmailUnsubscribe(); } public function reviewRequestSettings() { return (new EmailApi())->reviewRequestSettings(); } public function allReminderSettings($data) { return (new EmailApi())->allReminderSettings($data); } } PK �N,]vl7�0 �0 SettingService.phpnu ��� <?php namespace Rvx\Services; use Rvx\Api\SettingApi; class SettingService extends \Rvx\Services\Service { protected $settingApi; public function __construct() { // $this->settingApi = new SettingApi(); } public function getApiReviewSettings($data) { return (new SettingApi())->getApiReviewSettings($data); } public function saveApiReviewSettings($data) { return (new SettingApi())->saveApiReviewSettings($data); } public function getApiWidgetSettings() { return (new SettingApi())->getAPiWidgetSettings(); } public function saveWidgetSettings($data) { return (new SettingApi())->saveApiWidgetSettings($data); } /** * Get Settings Data * @return array */ public function getSettingsData($post_type = null) : array { $review_settings = $this->getReviewSettings($post_type); $widget_settings = $this->getWidgetSettings(); $rvx_settings = $this->formatSettings($review_settings, $widget_settings); // Ensure we always return an array even if invalid data exists return \is_array($rvx_settings) ? $rvx_settings : []; } public function getReviewSettings($post_type = null) : array { $default_cpt_name = 'product'; if ($post_type !== null) { $default_cpt_name = $post_type; } $option_name = '_rvx_settings_' . $default_cpt_name; $rvx_settings = \get_option($option_name, \false); if ($post_type === 'product' && $rvx_settings === \false) { $rvx_settings = \get_option('_rvx_settings_data'); } return $rvx_settings['setting']['review_settings'] ?? []; } public function getWidgetSettings() : array { $option_name = '_rvx_settings_widget'; $rvx_settings = \get_option($option_name, \false); if ($rvx_settings === \false) { $rvx_settings = \get_option('_rvx_settings_data'); } return $rvx_settings['setting']['widget_settings'] ?? []; } /** * Upadte Settings Data * @return array */ public function updateSettingsData(array $data, $post_type = null) : void { \update_option("_rvx_settings_data", $data); } public function updateReviewSettings(array $review_settings, $post_type = null) : void { $default_cpt_name = 'product'; if ($post_type !== null) { $default_cpt_name = $post_type; if ($post_type === 'product') { $review_settings = $review_settings['reviews']; } } $option_name = '_rvx_settings_' . $default_cpt_name; $data = ["setting" => ["review_settings" => ["reviews" => $review_settings]]]; if ($post_type !== 'product') { // Define the review submission policy $policy = ["review_submission_policy" => ["options" => ["anyone" => 1]]]; // Ensure reviews is an array and merge policy directly into it if (!\is_array($data['setting']['review_settings']['reviews'])) { $data['setting']['review_settings']['reviews'] = []; } // Merge the policy directly at the top level of "reviews" $data['setting']['review_settings']['reviews'] = \array_merge($policy, $data['setting']['review_settings']['reviews']); } \update_option($option_name, $data); } public function updateReviewSettingsOnSync(array $review_settings, $post_type = null) : void { $default_cpt_name = 'product'; if ($post_type !== null) { $default_cpt_name = $post_type; $review_settings = $review_settings['reviews']; } $option_name = '_rvx_settings_' . $default_cpt_name; $data = ["setting" => ["review_settings" => ["reviews" => $review_settings]]]; if ($post_type !== 'product') { // Define the review submission policy $policy = ["review_submission_policy" => ["options" => ["anyone" => 1]]]; // Ensure reviews is an array and merge policy directly into it if (!\is_array($data['setting']['review_settings']['reviews'])) { $data['setting']['review_settings']['reviews'] = []; } // Merge the policy directly at the top level of "reviews" $data['setting']['review_settings']['reviews'] = \array_merge($policy, $data['setting']['review_settings']['reviews']); } \update_option($option_name, $data); } public function updateWidgetSettings(array $widget_settings) : void { $data = ["setting" => ["widget_settings" => $widget_settings]]; \update_option("_rvx_settings_widget", $data); } private function formatSettings(array $review_settings, array $widget_settings) : array { $data = ["setting" => ["review_settings" => $review_settings, "widget_settings" => $widget_settings]]; return $data ?? []; } public function wooCommerceVerificationRating() : array { $value = \get_option('woocommerce_review_rating_verification_label', 'no'); return ['active' => $value === 'yes']; } public function wooVerificationRatingRequired() : array { $value = \get_option('woocommerce_review_rating_verification_required', 'no'); return ['active' => $value === 'yes']; } public function wooCommerceVerificationRatingUpdate($data) { if ($data['active'] == \true) { \update_option('woocommerce_review_rating_verification_label', 'yes'); $data = ['success' => \true, 'message' => __("Verified Owner Active")]; return $data; } if ($data['active'] == \false) { \update_option('woocommerce_review_rating_verification_label', 'no'); $data = ['success' => \true, 'message' => __("Verified Owner Deactive")]; return $data; } } public function wooVerificationRating($data) { if ($data['active'] == \true) { \update_option('woocommerce_review_rating_verification_required', 'yes'); $data = ['success' => \true, 'message' => __("Reviews can only be left by verified owners active")]; return $data; } if ($data['active'] == \false) { \update_option('woocommerce_review_rating_verification_required', 'no'); $data = ['success' => \true, 'message' => __("Reviews can only be left by verified owners deactive")]; return $data; } } public function userCurrentPlan() { return (new SettingApi())->userCurrentPlan(); } public function getApiGeneralSettings() { return (new SettingApi())->getApiGeneralSettings(); } public function saveApiGeneralSettings($data) { return (new SettingApi())->saveApiGeneralSettings($data); } public function allSettingsSave($data) { $payload_json = \json_encode($data['settings']); \update_option('rvx_all_setting_data', $payload_json); return ['message' => __('Settings saved successfully'), 'data' => $data['settings']]; } public function removeCredentials($requestData) { global $wpdb; $table_name = $wpdb->prefix . 'rvx_sites'; $sql = "TRUNCATE TABLE {$table_name}"; $result = $wpdb->query($sql); if ($wpdb->last_error) { return ['message' => 'Error: ' . $wpdb->last_error]; } return ['message' => 'Site Table deleted successfully', 'result' => $result]; } public function updateSiteData($requestHeaders) { global $wpdb; $table_name = $wpdb->prefix . 'rvx_sites'; // --- Extract headers (headers come as arrays) --- $user_email = isset($requestHeaders['x_user_email'][0]) ? sanitize_email($requestHeaders['x_user_email'][0]) : ''; $user_name = isset($requestHeaders['x_user_name'][0]) ? sanitize_text_field($requestHeaders['x_user_name'][0]) : ''; $site_uid = isset($requestHeaders['x_site_uid'][0]) ? sanitize_text_field($requestHeaders['x_site_uid'][0]) : ''; // $site_id = isset($requestHeaders['x_site_id'][0]) ? intval($requestHeaders['x_site_id'][0]) : 0; // $domain = isset($requestHeaders['x_domain'][0]) ? sanitize_text_field($requestHeaders['x_domain'][0]) : ''; // --- Validation: must provide at least one field to update --- if (empty($user_email) && empty($user_name)) { // error_log('[ReviewX] updateSiteData: Missing X-User-Email and X-User-Name in request headers.'); return ['status' => 'fail', 'message' => 'Missing X-User-Email and X-User-Name in request headers.']; } // --- Decide which identifier to use for WHERE (preferred order: uid, site_id, domain) --- $where = []; $where_fmt = []; if (!empty($site_uid)) { $where = ['uid' => $site_uid]; $where_fmt = ['%s']; } else { // No identifier provided — refuse to do a global update for safety // error_log('[ReviewX] updateSiteData: No site identifier provided (x_site_uid, x_site_id or x_domain). Aborting to avoid global update.'); return ['status' => 'fail', 'message' => 'Missing site identifier. Provide x_site_uid, x_site_id or x_domain in request headers.']; } // --- Prepare data to update --- $data = []; $format = []; if (!empty($user_email)) { $data['email'] = $user_email; $format[] = '%s'; } if (!empty($user_name)) { $data['name'] = $user_name; $format[] = '%s'; } if (empty($data)) { return ['status' => 'fail', 'message' => 'No valid update fields provided.']; } // error_log('[ReviewX] updateSiteData: Target WHERE: ' . print_r($where, true) . ' — Updating: ' . print_r($data, true)); // --- Fetch existing row to detect no-op updates and to ensure the row exists --- $where_keys = \array_keys($where); // Build a safe WHERE clause and prepare values $where_clauses = []; $where_values = []; foreach ($where as $col => $val) { $where_clauses[] = "{$col} = %s"; $where_values[] = (string) $val; } $where_sql = \implode(' AND ', $where_clauses); $select_sql = $wpdb->prepare("SELECT * FROM {$table_name} WHERE {$where_sql} LIMIT 1", $where_values); $existing = $wpdb->get_row($select_sql, ARRAY_A); if (null === $existing) { // error_log('[ReviewX] updateSiteData: No site row found for identifier: ' . print_r($where, true)); return ['status' => 'fail', 'message' => 'No site found matching provided identifier.', 'where' => $where]; } // Compare values — if identical, return success (no change needed) $is_same = \true; foreach ($data as $col => $val) { $existing_val = isset($existing[$col]) ? (string) $existing[$col] : ''; if ($existing_val !== (string) $val) { $is_same = \false; break; } } if ($is_same) { return ['status' => 'success', 'message' => 'No changes required — data already up to date.', 'data' => $data, 'where' => $where]; } // --- Perform the update using $wpdb->update (safe) --- $updated = $wpdb->update($table_name, $data, $where, $format, $where_fmt); if ($wpdb->last_error) { // error_log('[ReviewX] updateSiteData: DB error - ' . $wpdb->last_error); return ['status' => 'error', 'message' => 'Database error: ' . $wpdb->last_error]; } // $updated can be: false (error), 0 (no rows changed), >0 (rows updated) if ($updated === \false) { return ['status' => 'error', 'message' => 'Failed to update site data.']; } $rows = $wpdb->rows_affected; return ['status' => 'success', 'message' => $rows > 0 ? "Site data updated successfully ({$rows} row(s) affected)." : 'No rows were changed.', 'data' => $data, 'where' => $where]; } public function getLocalSettings($post_type) { return (new SettingApi())->getLocalSettings($post_type); } } PK �N,]�X��_ _ DashboardServices.phpnu ��� <?php namespace Rvx\Services; use Rvx\Api\DashboardApi; class DashboardServices extends \Rvx\Services\Service { public function insight() { return (new DashboardApi())->insightReviews(); } public function requestEmail() { return (new DashboardApi())->requestEmail(); } public function requestUserData() { global $wpdb; $table_name = $wpdb->prefix . 'rvx_sites'; $site_data = $wpdb->get_row("SELECT * FROM {$table_name} WHERE id = 1"); // error_log('Site Data: ' . print_r($site_data, true)); if (!$site_data) { return ['success' => \false, 'message' => 'No site data found', 'data' => null]; } return ['success' => \true, 'message' => 'Site user data fetched successfully', 'site_id' => $site_data->site_id, 'user_name' => $site_data->name, 'user_email' => $site_data->email, 'site_domain' => $site_data->domain, 'is_saas_synced' => $site_data->is_saas_sync]; } public function chart($request) { $time = $request['view']; return (new DashboardApi())->chart($time); } } PK �N,]��(�* * DataSyncService.phpnu ��� <?php namespace Rvx\Services; use Exception; use Rvx\Api\DataSyncApi; use Rvx\Api\WebhookRequestApi; use Rvx\Handlers\DataSyncHandler; use Rvx\Services\Service; use Rvx\Services\OrderService; use Rvx\Services\OrderItemSyncService; use Rvx\Services\UserSyncService; use Rvx\Services\ProductSyncService; use Rvx\Services\ReviewSyncService; // use Rvx\Services\CategorySyncService; use Rvx\Utilities\Helper; class DataSyncService extends Service { protected DataSyncHandler $dataSyncHandler; protected UserSyncService $userSyncService; // protected CategorySyncService $categorySyncService; protected ProductSyncService $productSyncService; protected ReviewSyncService $reviewSyncService; protected OrderService $orderService; protected OrderItemSyncService $orderItemSyncService; public function __construct() { $this->dataSyncHandler = new DataSyncHandler(); $this->userSyncService = new UserSyncService(); // $this->categorySyncService = new CategorySyncService(); $this->productSyncService = new ProductSyncService(); $this->reviewSyncService = new ReviewSyncService(); $this->orderService = new OrderService(); $this->orderItemSyncService = new OrderItemSyncService(); } public function dataSync($from, $post_type = 'product') : bool { try { $reviewx_dir_exists = \is_dir(WP_CONTENT_DIR . '/uploads/reviewx'); if (!$reviewx_dir_exists) { // Create the directory if it does not exist \mkdir(WP_CONTENT_DIR . '/uploads/reviewx', 0777, \true); } // Create the file path for the sync data // Sanitize just in case: $post_type = sanitize_key($post_type); if ($post_type === 'product') { $file_name = "shop-bulk-data.jsonl"; } else { $file_name = "{$post_type}-cpt-bulk-data.jsonl"; } $file_path = WP_CONTENT_DIR . '/uploads/reviewx/' . $file_name; $file = \fopen($file_path, 'w'); $total_objects = 0; if ($post_type === 'product') { // Product Sync Data for *Login or Register* $total_objects += $this->userSyncService->syncUser($file); if (\class_exists('WooCommerce') || $this->dataSyncHandler->wc_data_exists_in_db()) { // $syncedCaterories = new CategorySyncService(); // $total_objects += $syncedCaterories->syncCategory($file); $total_objects += $this->productSyncService->processProductForSync($file, $post_type); $total_objects += $this->reviewSyncService->processReviewForSync($file, $post_type); $total_objects += $this->orderItemSyncService->syncOrder($file); $total_objects += $this->orderItemSyncService->syncOrderItem($file); } } else { // CPT Sync Data $total_objects += $this->productSyncService->processProductForSync($file, $post_type); $total_objects += $this->reviewSyncService->processReviewForSync($file, $post_type); } \fclose($file); (new WebhookRequestApi())->finishedWebhook(['total_objects' => $total_objects, 'status' => 'finished', 'from' => $from, 'post_type' => $post_type, 'resource_url' => Helper::getRestAPIurl() . '/api/v1/synced/data?post_type=' . $post_type]); return \true; } catch (Exception $e) { return \false; } } protected function dataSyncFile($file, $file_path, $from, $total_objects) { \fclose($file); $file_info = $this->prepareFileInfo($file_path); $file = $_FILES['file'] = $file_info; $fileUpload = (new DataSyncApi())->dataSync($file, $from, $total_objects); if (\file_exists($file_path)) { \unlink($file_path); } return $fileUpload; } private function prepareFileInfo($file_path) { return ['name' => \basename($file_path), 'full_path' => \realpath($file_path), 'type' => "application/json", 'tmp_name' => $file_path, 'error' => 0, 'size' => \filesize($file_path)]; } public function syncStatus() { return (new DataSyncApi())->syncStatus(); } public function dataManualSync($data) { \mkdir(WP_CONTENT_DIR . '/uploads/reviewx', 0777, \true); $file_path = WP_CONTENT_DIR . '/uploads/reviewx/manual_sync.jsonl'; $file = \fopen($file_path, 'a'); $totalLines = 0; if ("users" === $data['action']) { $totalLines = \get_option('rvx_sync_number'); $totalLines += (new UserSyncService())->syncUser($file); \update_option('rvx_sync_number', $totalLines); } if ("categories" === $data['action']) { if (\class_exists('WooCommerce') || $this->dataSyncHandler->wc_data_exists_in_db()) { // $syncedCaterories = new CategorySyncService(); // $totalLines += $syncedCaterories->syncCategory($file); $processProduct = new ProductSyncService(); $totalLines += $processProduct->processProductForSync($file, 'product'); \update_option('rvx_sync_number', $totalLines); } } if ("reviews" === $data['action']) { if (\class_exists('WooCommerce') || $this->dataSyncHandler->wc_data_exists_in_db()) { $totalLines = \get_option('rvx_sync_number'); $totalLines += (new ReviewSyncService())->processReviewForSync($file, 'product'); \update_option('rvx_sync_number', $totalLines); } } if ("order" === $data['action']) { if (\class_exists('WooCommerce') || $this->dataSyncHandler->wc_data_exists_in_db()) { $order = new OrderItemSyncService(); $totalLines = \get_option('rvx_sync_number'); $totalLines += $order->syncOrder($file); $totalLines += $order->syncOrderItem($file); \update_option('rvx_sync_number', $totalLines); } } if ("api" === $data['action']) { $totalLines = \get_option('rvx_sync_number'); return $this->dataSyncFile($file, $file_path, 'register', $totalLines); } } } PK �N,]n�fR� � DiscountSyncService.phpnu ��� <?php namespace Rvx\Services; use Exception; use Rvx\Utilities\Auth\Client; use Rvx\Utilities\Helper; use Rvx\WPDrill\Facades\DB; class DiscountSyncService extends \Rvx\Services\Service { protected $discountCount = 0; protected $usedbyRelation; protected $maximumAmountRelation; protected $minimumAmountRelation; protected $freeShippingRelation; protected $dateExpiresRelation; protected $usageLimitPerUserRelation; protected $usageLimitRelation; protected $couponAmountRelation; protected $discountTypeRelation; public function processDiscountForSync($file) : int { $this->syncPostMeta(); return $this->syncPost($file); } public function syncPostMeta() { try { $this->usedbyRelation = []; $this->maximumAmountRelation = []; $this->minimumAmountRelation = []; $this->freeShippingRelation = []; $this->dateExpiresRelation = []; $this->usageLimitPerUserRelation = []; $this->usageLimitRelation = []; $this->couponAmountRelation = []; $this->discountTypeRelation = []; DB::table('postmeta')->whereIn('meta_key', ['_used_by', 'maximum_amount', 'minimum_amount', 'free_shipping', 'date_expires', 'usage_limit_per_user', 'usage_limit', 'coupon_amount', 'discount_type'])->chunk(100, function ($allPostMeta) { foreach ($allPostMeta as $postMetas) { switch ($postMetas->meta_key) { case '_used_by': $this->usedbyRelation[$postMetas->post_id] = $postMetas->meta_value; break; case 'maximum_amount': $this->maximumAmountRelation[$postMetas->post_id] = $postMetas->meta_value; break; case 'minimum_amount': $this->minimumAmountRelation[$postMetas->post_id] = $postMetas->meta_value; break; case 'free_shipping': $this->freeShippingRelation[$postMetas->post_id] = $postMetas->meta_value; break; case 'date_expires': $this->dateExpiresRelation[$postMetas->post_id] = $postMetas->meta_value; break; case 'usage_limit_per_user': $this->usageLimitPerUserRelation[$postMetas->post_id] = $postMetas->meta_value; break; case 'usage_limit': $this->usageLimitRelation[$postMetas->post_id] = $postMetas->meta_value; break; case 'coupon_amount': $this->couponAmountRelation[$postMetas->post_id] = $postMetas->meta_value; break; case 'discount_type': $this->discountTypeRelation[$postMetas->post_id] = $postMetas->meta_value; break; } } }); } catch (Exception $e) { throw new Exception($e->getMessage()); } } public function syncPost($file) { $discountCount = 0; DB::table('posts')->select(['ID', 'post_type', 'post_title', 'post_name', 'post_status', 'post_modified'])->orderBy('ID')->whereIn('post_type', ['shop_coupon'])->chunk(100, function ($discounts) use(&$file, &$discountCount) { foreach ($discounts as $discount) { $formatedDiscount = $this->processDiscount($discount); if ($formatedDiscount['single_code'] && $formatedDiscount['type']) { Helper::appendToJsonl($file, $formatedDiscount); $discountCount++; } } }); Helper::rvxLog($discountCount, "Cuopn Done"); return $discountCount; } public function processDiscount($discount) : array { $start_date = \wp_date('Y-m-d', \strtotime($discount->post_modified)) ?? null; $end_date = \wp_date('Y-m-d', $this->dateExpiresRelation[$discount->ID]) ?? null; $exper_day = $this->discountExperDay($start_date, $end_date); return ['rid' => 'rid://Discount/' . (int) $discount->ID, "request_type" => 2, "wp_id" => (int) $discount->ID, "wp_unique_id" => Client::getUid() . '-' . (int) $discount->ID, "site_id" => Client::getSiteId(), "code_type" => 1, "status" => $this->discountStatus($discount->post_status), "single_code" => !empty($discount->post_title) ? $discount->post_title : null, "title" => !empty($discount->post_title) ? $discount->post_title : null, "type" => $this->discountType($this->discountTypeRelation[$discount->ID]) ?? null, "value" => 6, "free_shipping" => $this->dataTypeConvert($this->freeShippingRelation[$discount->ID]) ?? \false, "can_be_used_with_other_coupon" => \false, "exclude_sale_items_from_discount" => \true, "minimum_amount" => Helper::formatToTwoDecimalPlaces($this->minimumAmountRelation[$discount->ID] ?? 0), "maximum_amount" => Helper::formatToTwoDecimalPlaces($this->maximumAmountRelation[$discount->ID] ?? 0), "usage_limit_per_coupon" => (int) $this->usageLimitPerUserRelation[$discount->ID] ?? 0, "usage_limit_per_user" => (int) $this->usageLimitRelation[$discount->ID] ?? 0, "start_date" => $start_date, "start_time" => \wp_date('H:i', \strtotime($discount->post_modified)) ?? null, "expires_in" => $exper_day, "end_date" => $end_date, "end_time" => \wp_date('H:i', $this->dateExpiresRelation[$discount->ID]) ?? null]; } public function discountStatus($status) : int { switch ($status) { case 'publish': return 1; case 'private': return 2; default: return 3; } } public function discountType($type) : string { switch ($type) { case 'fixed_cart': return 'fixed_amount'; case 'percent': return 'percentage'; default: return ''; } } public function dataTypeConvert($data) : bool { if ($data === 'yes') { return \true; } return \false; } public function discountExperDay($start_date, $end_date) { if ($start_date && $end_date) { $start_timestamp = \strtotime($start_date); $end_timestamp = \strtotime($end_date); $difference_in_seconds = $end_timestamp - $start_timestamp; return \floor($difference_in_seconds / (60 * 60 * 24)); } return null; } } PK �N,]ɢ�8� � OrderService.phpnu ��� <?php namespace Rvx\Services; use Rvx\Api\OrderApi; use Rvx\Utilities\Auth\Client; use Rvx\Utilities\Helper; use Rvx\WPDrill\Response; class OrderService extends \Rvx\Services\Service { public function __construct() { } public function updateOrder($order_id) { $order = wc_get_order($order_id); if (!$order) { return; } $payload = $this->prepareData($order); $uid = Client::getUid() . '-' . $order_id; $response = (new OrderApi())->update($payload, $uid); if ($response->getStatusCode() !== Response::HTTP_OK) { \error_log('Order Not Update' . $response->getStatusCode()); return \false; } } public function prepareData($order) { $status = $order->get_status(); $status_mapping = $this->orderStatusArray(); $date_created = $order->get_date_created(); $created_at = $date_created ? \wp_date('Y-m-d H:i:s', $date_created->getTimestamp()) : null; $date_modified = $order->get_date_modified(); $updated_at = $date_modified ? \wp_date('Y-m-d H:i:s', $date_modified->getTimestamp()) : \wp_date('Y-m-d H:i:s'); // Get the order state, ensure a fallback if 'date_paid' doesn't exist $order_state = $this->wooOrderState($order->get_id()); $paid_at = $order_state['date_paid'] ?? null; $orderData = ["wp_id" => (int) $order->get_id(), "customer_wp_unique_id" => Client::getUid() . '-' . (int) $order->get_customer_id(), "subtotal" => (float) $order->get_subtotal(), "tax" => (float) $order->get_total_tax(), "total" => (float) $order->get_total(), "status" => Helper::orderStatus($order->get_status()), "review_request_email_sent_at" => null, "review_reminder_email_sent_at" => null, "photo_review_email_sent_at" => null, "paid_at" => $paid_at, "created_at" => $created_at, "updated_at" => $updated_at]; if (isset($status_mapping[$status])) { $orderData[$status_mapping[$status]] = \wp_date('Y-m-d H:i:s'); } return ['order' => $orderData, 'order_items' => $this->orderItems($order, $orderData)]; } public function orderStatusArray() : array { return ['processing' => 'processing_at', 'pending_payment' => 'pending_payment_at', 'on_hold' => 'on_hold_at', 'completed' => 'completed_at', 'cancelled' => 'cancelled_at', 'refunded' => 'refunded_at', 'failed' => 'failed_at', 'draft' => 'draft_at']; } public function wooOrderState($order_id) { global $wpdb; $query = $wpdb->prepare("SELECT date_paid, date_completed FROM {$wpdb->prefix}wc_order_stats WHERE order_id = %d", $order_id); $results = $wpdb->get_row($query); if ($results) { return ['date_paid' => !empty($results->date_paid) && \strtotime($results->date_paid) ? \wp_date('Y-m-d H:i:s', \strtotime($results->date_paid)) : null, 'date_completed' => !empty($results->date_completed) && \strtotime($results->date_completed) ? \wp_date('Y-m-d H:i:s', \strtotime($results->date_completed)) : null]; } return ['date_paid' => null, 'date_completed' => null]; } public function orderItems($order, $orderData = []) { $date = $this->wooOrderState($order->get_id()); $items_data = []; $order_items = $order->get_items(); foreach ($order_items as $order_item) { $product = $order_item->get_product(); if ($product) { $item_data = ["wp_id" => (int) $order_item->get_id(), "wp_unique_id" => Client::getUid() . '-' . (int) $order_item->get_id(), "product_wp_unique_id" => Client::getUid() . '-' . (int) $product->get_id(), "review_id" => null, "site_id" => Client::getSiteId(), "name" => $product->get_name(), "quantity" => $order_item->get_quantity(), "price" => (float) $product->get_price(), "reviewed_at" => null, "fulfillment_status" => Helper::orderItemStatus($order->get_status())]; if ($order->get_status() !== 'completed') { $item_data['fulfilled_at'] = \wp_date('Y-m-d H:i:s'); } if ($order->get_status() == 'completed') { $item_data['fulfilled_at'] = !empty($date['date_completed']) && \strtotime($date['date_completed']) ? \wp_date('Y-m-d H:i:s', \strtotime($date['date_completed'])) : \wp_date('Y-m-d H:i:s'); } $items_data[] = $item_data; } } return $items_data; } } PK �N,]��< < CptService.phpnu ��� <?php namespace Rvx\Services; use Rvx\Apiz\Http\Response; use Rvx\Api\CptApi; use Rvx\Utilities\Auth\Client; class CptService { /** * @return Response */ public function cptGet() { return (new CptApi())->cptGet(); } public function cptStore($request) { $uid = ['wp_unique_id' => Client::getUid()]; $data = \array_merge($request, $uid); return (new CptApi())->cptStore($data); } public function cptUpdate($data) { $uid = $data['uid']; return (new CptApi())->cptUpdate($data, $uid); } public function cptDelete($data) { return (new CptApi())->cptDelete($data); } public function cptStatusChange($data) { $uid = $data['uid']; return (new CptApi())->cptStatusChange($data, $uid); } } PK �N,]eܠ�� � CategorySyncService.phpnu ��� <?php namespace Rvx\Services; use Rvx\Utilities\Auth\Client; use Rvx\Utilities\Helper; use Rvx\WPDrill\Facades\DB; use Rvx\Handlers\DataSyncHandler; class CategorySyncService extends \Rvx\Services\Service { protected $categories; protected $taxonomyRelation; protected $descriptionRelation; protected $parentRelation; protected $selectedTerms; protected $syncedCategories; protected $postTermRelation = []; protected $taxonomyTerm; protected $datSyncHandler; public function __construct() { $this->datSyncHandler = new DataSyncHandler(); } public function syncCategory($file) { $catCount = 0; $this->syncTermTaxonomy(); $this->syncTermTaxonomyRelation(); DB::table('terms')->chunk(100, function ($allTerms) use($file, &$catCount) { foreach ($allTerms as $term) { if (\in_array((int) $term->term_id, $this->selectedTerms, \true)) { $formatedTerm = $this->formatCategoryData($term); $this->setSyncCategories($formatedTerm); Helper::rvxLog($formatedTerm); Helper::appendToJsonl($file, $formatedTerm); $catCount++; } } }); Helper::rvxLog($catCount, "Category Done"); return $catCount; } public function getPostTermRelation() { return $this->postTermRelation; } public function setSyncCategories($syncedCategories) : void { $this->syncedCategories[] = $syncedCategories; } public function setPostTermRelation($postTermRelation) { return $this->postTermRelation = $postTermRelation; } public function syncTermTaxonomyRelation() : void { DB::table('term_relationships')->chunk(100, function ($allTermTaxonomyRelations) { foreach ($allTermTaxonomyRelations as $termTaxonomyRelation) { if (\array_key_exists($termTaxonomyRelation->object_id, $this->postTermRelation)) { $this->postTermRelation[$termTaxonomyRelation->object_id] = \array_merge($this->postTermRelation[$termTaxonomyRelation->object_id], [(int) $termTaxonomyRelation->term_taxonomy_id]); } else { $this->postTermRelation[$termTaxonomyRelation->object_id] = isset($this->taxonomyTerm[$termTaxonomyRelation->term_taxonomy_id]) ? [Helper::arrayGet($this->taxonomyTerm, $termTaxonomyRelation->term_taxonomy_id, [])] : []; } } }); $this->setPostTermRelation($this->postTermRelation); } public function syncTermTaxonomy() : void { DB::table('term_taxonomy')->select(['term_taxonomy_id', 'term_id', 'taxonomy', 'parent'])->whereIn('taxonomy', $this->datSyncHandler->getProductTaxonomies())->chunk(100, function ($allTermTaxonomy) { foreach ($allTermTaxonomy as $termTaxonomy) { $this->taxonomyTerm[$termTaxonomy->term_taxonomy_id] = (int) $termTaxonomy->term_id; $this->selectedTerms[] = (int) $termTaxonomy->term_id; $this->taxonomyRelation[$termTaxonomy->term_id] = $termTaxonomy->taxonomy; $this->parentRelation[$termTaxonomy->term_id] = $termTaxonomy->parent; } }); } private function formatCategoryData($category) : array { return ['rid' => 'rid://Category/' . (int) $category->term_id, 'wp_id' => (int) $category->term_id, 'title' => $category->name ?? null, 'slug' => $category->slug ?? null, 'taxonomy' => $this->taxonomyRelation[$category->term_id] ?? null, 'description' => null, 'parent_wp_unique_id' => isset($this->parentRelation[$category->term_id]) ? Client::getUid() . '-' . $this->parentRelation[$category->term_id] : '']; } } PK �N,]YX��� � GoogleReviewService.phpnu ��� <?php namespace Rvx\Services; use Rvx\Apiz\Http\Response; use Rvx\Api\GoogleReviewApi; class GoogleReviewService extends \Rvx\Services\Service { /** * @return Response */ public function googleReviewGet() { return (new GoogleReviewApi())->googleReviewGet(); } /** * @return Response */ public function googleReviewPlaceApi() { return (new GoogleReviewApi())->googleReviewPlaceApi(); } public function googleRecaptchaVerify($data) { $secret = (new \Rvx\Services\SettingService())->getReviewSettings()['reviews']['recaptcha']['secret_key']; $token = $data['token']; $recaptcha_url = 'https://www.google.com/recaptcha/api/siteverify?secret=' . \urlencode($secret) . '&response=' . \urlencode($token); $response = wp_remote_get($recaptcha_url); $body = wp_remote_retrieve_body($response); $result = \json_decode($body, \true); return ['result' => $result['success']]; } public function googleReviewKey($request) { return (new GoogleReviewApi())->googleReviewKey($request); } public function googleReviewSetting($request) { return (new GoogleReviewApi())->googleReviewSetting($request); } } PK �N,]���M �M ReviewSyncService.phpnu ��� <?php namespace Rvx\Services; use Rvx\Handlers\MigrationRollback\MigrationPrompt; use Rvx\Handlers\MigrationRollback\ReviewXChecker; use Rvx\Utilities\Helper; use Rvx\Utilities\Auth\Client; use Rvx\WPDrill\Facades\DB; use Rvx\Services\ReviewService; class ReviewSyncService extends \Rvx\Services\Service { protected $reviewMetaTitle; protected $reviewRelationId; protected $reviewids; protected $reviewMetaRating; protected $reviewMetaVerified; protected $reviewMetaAttachmentsAll; protected $reviewMetaRecommended; protected $reviewMetaAnonymous; protected $reviewMultiCriteriasRating; protected $reviewTrashStatus; protected $reviewTrashTime; protected $reviewMetaOrder; protected $reviewMetaOrderItem; protected $orderCustomerRelation = []; protected $criteria; protected $procesedReviews; protected $commentReplyRelation; protected ReviewService $reviewService; protected MigrationPrompt $migrationData; public function __construct() { $this->reviewService = new ReviewService(); $this->migrationData = new MigrationPrompt(); if (ReviewXChecker::isReviewXExists() && !ReviewXChecker::isReviewXSaasExists()) { $this->criteria = \get_option('_rx_option_review_criteria') ?? []; } elseif (ReviewXChecker::isReviewXSaasExists()) { $this->criteria = (new \Rvx\Services\SettingService())->getReviewSettings('product')['reviews']['multicriteria']["criterias"] ?? []; } else { $this->criteria = []; } } public function getCriteria() { return $this->criteria; } public function processReviewForSync($file, $post_type) : int { $this->syncReviewMata(); return $this->syncReview($file, $post_type); } public function syncReview($file, $post_type) : int { $this->procesedReviews = []; $this->reviewids = []; $this->reviewRelationId = []; $reviewCount = 0; //Reply DB::table('comments')->join('posts', 'posts.ID', '=', 'comments.comment_post_ID')->where('posts.post_type', $post_type)->where('comment_parent', '!=', 0)->chunk(100, function ($comments) use(&$file) { foreach ($comments as $comment) { $this->commentReplyRelation[$comment->comment_parent][] = [$comment->comment_ID => $comment->comment_content]; } }); //WC Reviews / CPT Reviews $review_type = $post_type === 'product' ? ['review'] : ['comment']; DB::table('comments')->join('posts', 'posts.ID', '=', 'comments.comment_post_ID')->where('posts.post_type', $post_type)->where('comment_parent', '=', 0)->whereIn('comment_type', $review_type)->chunk(100, function ($comments) use(&$commentReplyRelation, &$file, &$reviewCount) { foreach ($comments as $comment) { $this->procesedReviews = $this->processReview($comment); Helper::appendToJsonl($file, $this->procesedReviews); $reviewCount++; } }); // Helper::rvxLog($reviewCount, "Review Done"); return $reviewCount; } public function processReview($comment) : array { $reply = null; if (!empty($this->commentReplyRelation[$comment->comment_ID]) && \is_array($this->commentReplyRelation[$comment->comment_ID])) { $replyData = $this->commentReplyRelation[$comment->comment_ID][0] ?? null; $reply = $replyData ? \reset($replyData) : null; } $trashed_at = null; if ($comment->comment_approved === 'trash') { $status = !empty($this->reviewTrashStatus[$comment->comment_ID]) && $this->reviewTrashStatus[$comment->comment_ID] === 0 ? 'pending' : 'published'; $metaTrashTime = $this->reviewTrashTime[$comment->comment_ID] ?? null; $trashed_at = $metaTrashTime ? Helper::validateReturnDate($metaTrashTime) : null; } else { $status = $this->getCommentStatus($comment); } return ['rid' => 'rid://Review/' . (int) $comment->comment_ID, 'product_id' => (int) $comment->comment_post_ID, 'wp_id' => (int) $comment->comment_ID, 'wp_post_id' => (int) $comment->comment_post_ID, 'rating' => isset($this->reviewMetaRating[$comment->comment_ID]) ? (float) \round($this->reviewMetaRating[$comment->comment_ID], 2) : (float) 0.0, 'reviewer_email' => $comment->comment_author_email ?? null, 'reviewer_name' => $comment->comment_author ?? null, 'title' => isset($this->reviewMetaTitle[$comment->comment_ID]) ? $this->reviewMetaTitle[$comment->comment_ID] : null, 'feedback' => $comment->comment_content ?? null, 'verified' => !empty($this->reviewMetaVerified[$comment->comment_ID]), 'attachments' => $this->reviewMetaAttachmentsAll[$comment->comment_ID] ?? [], 'is_recommended' => !empty($this->reviewMetaRecommended[$comment->comment_ID]), 'is_anonymous' => !empty($this->reviewMetaAnonymous[$comment->comment_ID]), 'status' => $status, 'reply' => $reply, 'trashed_at' => $trashed_at, 'created_at' => Helper::validateReturnDate($comment->comment_date_gmt) ?? null, 'customer_id' => $this->getReviewCustomerId($comment), 'order_wp_unique_id' => isset($this->reviewMetaOrder[$comment->comment_ID]) ? Client::getUid() . '-' . $this->reviewMetaOrder[$comment->comment_ID] : null, 'order_item_wp_unique_id' => $this->reviewMetaOrderItem[$comment->comment_ID] ?? null, 'ip' => $comment->comment_author_IP ?? null, 'criterias' => $this->reviewMultiCriteriasRating[$comment->comment_ID] ?? null]; } private function getReviewCustomerId($comment) : ?string { $orderId = $this->reviewMetaOrder[$comment->comment_ID] ?? null; // If we have an order ID, try to get the customer from the order relation if ($orderId && isset($this->orderCustomerRelation[(int) $orderId])) { $customerId = $this->orderCustomerRelation[(int) $orderId]; return Client::getUid() . '-' . $customerId; } // Fallback to comment user_id, but only if it's not likely to be an admin if ($comment->user_id) { // For now, we'll trust user_id if no order is linked. return Client::getUid() . '-' . $comment->user_id; } return null; } public function syncReviewMata() : void { DB::table('commentmeta')->whereIn('meta_key', ['rvx_review_version', 'reviewx_title', 'verified', 'rating', 'rvx_criterias', 'reviewx_rating', 'reviewx_attachments', 'reviewx_video_url', 'is_recommended', 'reviewx_recommended', 'is_anonymous', '_wp_trash_meta_status', '_wp_trash_meta_time', 'reviewx_order', 'rvx_comment_order_item'])->chunk(100, function ($allCommentMeta) { $orderIds = []; foreach ($allCommentMeta as $commentMeta) { $commentId = $commentMeta->comment_id; if ($commentMeta->meta_key === 'reviewx_order') { $this->reviewMetaOrder[$commentId] = $commentMeta->meta_value; $orderIds[] = (int) $commentMeta->meta_value; } if ($commentMeta->meta_key === 'rvx_comment_order_item') { $this->reviewMetaOrderItem[$commentId] = $commentMeta->meta_value; } // Process each meta_key if ($commentMeta->meta_key === 'reviewx_title') { $this->reviewMetaTitle[$commentId] = $commentMeta->meta_value; } if ($commentMeta->meta_key === 'verified') { $this->reviewMetaVerified[$commentId] = !\in_array($commentMeta->meta_value, ['', '0', 'false', 0, \false], \true); } if ($commentMeta->meta_key === 'rating') { $this->reviewMetaRating[$commentId] = $commentMeta->meta_value; if (!ReviewXChecker::isReviewXExists() && !ReviewXChecker::isReviewXSaasExists()) { $this->reviewMultiCriteriasRating[$commentId] = $this->criteriaMappingWC($commentId, $commentMeta->meta_value); } } if (ReviewXChecker::isReviewXExists() && !ReviewXChecker::isReviewXSaasExists()) { if ($commentMeta->meta_key === 'reviewx_rating') { $this->reviewMultiCriteriasRating[$commentId] = $this->criteriaMappingV1($commentId, $commentMeta->meta_value); } if (\in_array($commentMeta->meta_key, ['reviewx_attachments', 'reviewx_video_url'], \true)) { $metaData = ['reviewx_attachments' => $commentMeta->meta_key === 'reviewx_attachments' ? $commentMeta->meta_value : null, 'reviewx_video_url' => $commentMeta->meta_key === 'reviewx_video_url' ? $commentMeta->meta_value : null]; // Call attachmentsV1 once for the current $commentId $this->reviewMetaAttachmentsAll[$commentId] = $this->attachmentsV1($commentId, $metaData); } if ($commentMeta->meta_key === 'reviewx_recommended') { $this->reviewMetaRecommended[$commentId] = !\in_array($commentMeta->meta_value, ['', '0', 'false', 0, \false], \true); } if ($commentMeta->meta_key === 'reviewx_anonymous') { $this->reviewMetaAnonymous[$commentId] = !\in_array($commentMeta->meta_value, ['', '0', 'false', 0, \false], \true); } } if (ReviewXChecker::isReviewXSaasExists()) { if ($commentMeta->meta_key === 'rvx_criterias') { $this->reviewMultiCriteriasRating[$commentId] = $this->criteriaMappingV2($commentId, $commentMeta->meta_value); } if ($commentMeta->meta_key === 'reviewx_attachments') { $this->reviewMetaAttachmentsAll[$commentId] = $this->attachmentsV2($commentId, $commentMeta->meta_value); } if ($commentMeta->meta_key === 'is_recommended') { $this->reviewMetaRecommended[$commentId] = $commentMeta->meta_value; } if ($commentMeta->meta_key === 'is_anonymous') { $this->reviewMetaAnonymous[$commentId] = $commentMeta->meta_value; } } if ($commentMeta->meta_key === '_wp_trash_meta_status') { $this->reviewTrashStatus[$commentId] = $commentMeta->meta_value; } if ($commentMeta->meta_key === '_wp_trash_meta_time') { $this->reviewTrashTime[$commentId] = $commentMeta->meta_value; } } // Pre-fetch customer IDs for found orders to avoid N+1 queries if (!empty($orderIds)) { $orderStats = DB::table('wc_order_stats')->whereIn('order_id', \array_unique($orderIds))->select(['order_id', 'customer_id'])->get(); foreach ($orderStats as $stat) { $this->orderCustomerRelation[(int) $stat->order_id] = (int) $stat->customer_id; } } }); } private function getCommentStatus($comment) : ?string { switch ($comment->comment_approved) { case '1': return 'published'; case '0': return 'pending'; case 'spam': return 'spam'; default: return null; } } private function criteriaMappingWC($commentId, $metaValue) { $metaValue = maybe_unserialize($metaValue) ?? 0; // If $metaValue is not an array or scalar numeric, default to 0 if (!\is_array($metaValue) && !\is_numeric($metaValue)) { $metaValue = 0; } // If it's a scalar, treat as single rating and wrap in array for consistency if (\is_numeric($metaValue)) { $metaValue = [$metaValue]; } // Predefined keys a to j (10 keys) $keys = ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j"]; $newArray = []; $i = 0; foreach ($keys as $key) { // Always add predefined keys, with value from $metaValue or 0 $newArray[$key] = isset($metaValue[$i]) ? (int) $metaValue[$i] : 0; $i++; } if ($newArray == []) { return $newArray = null; } // Update the comment meta with the new format if (!empty($newArray)) { \update_comment_meta($commentId, 'rvx_criterias', $newArray); } return $newArray; } private function criteriaMappingV1($commentId, $metaValue) { $metaValue = maybe_unserialize($metaValue); // Example: a:3:{s:8:"ctr_h8S7";s:1:"3";s:8:"ctr_h8S8";s:1:"3";s:8:"ctr_h8S9";s:1:"3";} // Retrieve the existing criteria mapping (old criteria names) $multCritria_data = $this->getCriteria(); // Example: a:3:{s:8:"ctr_h8S7";s:7:"Quality";s:8:"ctr_h8S8";s:5:"Price";s:8:"ctr_h8S9";s:9:"Packaging";} if (empty($multCritria_data)) { return null; // If no criteria data is available, return an empty array } // Flip multCritria_data to map keys like 'ctr_h8S7' => 'a', 'ctr_h8S8' => 'b', etc. $criteriaKeys = []; $index = 0; foreach ($multCritria_data as $key => $name) { // Assign a short key (a, b, c, ...) based on order $criteriaKeys[$key] = \chr(97 + $index); // ASCII 'a' = 97 $index++; } // Initialize the new criteria array in the required format $newCriteria = []; if (!empty($metaValue)) { foreach ($metaValue as $key => $value) { if (isset($criteriaKeys[$key])) { // Use the new short key and map it to the value as an integer $newCriteria[$criteriaKeys[$key]] = (int) $value; // 'a' => 3, 'b' => 2 } } } else { foreach ($criteriaKeys as $shortKey) { // If metaValue is empty, assign default value of 0 $newCriteria[$shortKey] = 0; // 'a' => 0, 'b' => 0 } } // Update the 'rating' comment meta with the new format $ratingValue = \get_comment_meta($commentId, 'rating', \true); $currentRating = (float) \is_numeric($ratingValue) ? (float) \round($ratingValue, 2) : (float) 0.0; $averageRating = $this->reviewService->calculateAverageRating($newCriteria); $critriaAllowed = $this->migrationData->rvx_retrieve_old_plugin_options_data()['multicriteria']['enable'] ?? \false; if ($currentRating !== $averageRating && !empty($metaValue) && $critriaAllowed === \true) { \update_comment_meta($commentId, 'rating', $averageRating); } // Fill in missing keys ('a' to 'j') with default value 0 $allKeys = ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j"]; foreach ($allKeys as $key) { if (!isset($newCriteria[$key])) { $newCriteria[$key] = 0; // Default value for missing keys } } // Update the 'rvx_criterias' comment meta with the new format if (!empty($newCriteria)) { \update_comment_meta($commentId, 'rvx_criterias', $newCriteria); } return $newCriteria; } private function criteriaMappingV2($commentId, $metaValue) { // Deserialize the meta value $metaValue = maybe_unserialize($metaValue); // Ensure $metaValue is always an array if (!\is_array($metaValue)) { $metaValue = []; } // Retrieve the criteria mapping $multCritria_data = $this->getCriteria(); if (empty($multCritria_data)) { return null; // No criteria data available } // Initialize the new criteria array $newCriteria = $metaValue; // Start with existing metaValue if valid // Update the 'rating' comment meta with the new format $ratingValue = \get_comment_meta($commentId, 'rating', \true); $currentRating = (float) \is_numeric($ratingValue) ? (float) \round($ratingValue, 2) : (float) 0.0; $averageRating = $this->reviewService->calculateAverageRating($newCriteria); $critriaAllowed = $this->migrationData->rvx_retrieve_saas_plugin_options_data()['multicriteria']['enable'] ?? \false; if ($currentRating !== $averageRating && !empty($metaValue) && $critriaAllowed === \true) { \update_comment_meta($commentId, 'rating', $averageRating); } // Fill in missing keys ('a' to 'j') with default value 0 $allKeys = ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j"]; foreach ($allKeys as $key) { if (!isset($newCriteria[$key])) { $newCriteria[$key] = 0; // Default value for missing keys } else { // Ensure all existing values are integers $newCriteria[$key] = (int) $newCriteria[$key]; } } // Update the comment meta with the new format if (!empty($newCriteria)) { \update_comment_meta($commentId, 'rvx_criterias', $newCriteria); } return $newCriteria; // Ensure all keys from 'a' to 'j' are present } private function attachmentsV1($commentId, array $metaData) : array { $links = []; foreach ($metaData as $metaKey => $metaValue) { if ($metaValue === null) { continue; // Skip if no value provided for this meta key } $data = \is_string($metaValue) ? maybe_unserialize($metaValue) : $metaValue; if ($metaKey === 'reviewx_attachments' && \is_array($data) && isset($data['images'])) { // Process image attachments foreach ($data['images'] as $image_id) { $image_url = wp_get_attachment_url($image_id); if (\filter_var($image_url, \FILTER_VALIDATE_URL)) { $links[] = $image_url; } } } if ($metaKey === 'reviewx_video_url') { // Process video attachments $videoLinks = []; if (\is_array($data)) { foreach ($data as $video_url) { if (\filter_var($video_url, \FILTER_VALIDATE_URL)) { $videoLinks[] = $video_url; } } } elseif (\is_string($data) && \filter_var($data, \FILTER_VALIDATE_URL)) { $videoLinks[] = $data; } // Merge video links into links $links = \array_merge($links, $videoLinks); } } // Update the comment meta with IDs for images and URLs for videos if (!empty($links)) { \update_comment_meta($commentId, 'reviewx_attachments', $links); } return $links; // Return URLs } private function attachmentsV2($commentId, $metaValue) : array { $data = maybe_unserialize($metaValue); $links = []; if (\is_array($data)) { foreach ($data as $data_url) { if (\filter_var($data_url, \FILTER_VALIDATE_URL)) { $links[] = $data_url; } } } elseif (\is_string($data) && \filter_var($data, \FILTER_VALIDATE_URL)) { $links[] = $data; } return $links; } } PK �N,]7W��m m CacheServices.phpnu ��� <?php namespace Rvx\Services; class CacheServices extends \Rvx\Services\Service { public function allReviewApproveCount() : int { global $wpdb; $query = $wpdb->prepare("SELECT COUNT(*) \n FROM {$wpdb->comments} \n WHERE comment_approved = '1' \n AND comment_parent = 0 \n AND comment_type IN ('review','comment')"); return (int) $wpdb->get_var($query); } public function allReviewPendingCount() : int { global $wpdb; $query = $wpdb->prepare("SELECT COUNT(*) \n FROM {$wpdb->comments} \n WHERE comment_approved = '0' \n AND comment_parent = 0\n AND comment_type IN ('review','comment')"); return (int) $wpdb->get_var($query); } public function saasStatusReviewCount() { $data = \get_transient('rvx_reviews_data_list'); if (\is_array($data)) { return $data['count']; } return []; } public function makeSaaSCallDecision() { $approveReviewCount = $this->allReviewApproveCount(); $pendingReviewCount = $this->allReviewPendingCount(); $saasApproveReviewCount = \array_key_exists('published', $this->saasStatusReviewCount()) ? $this->saasStatusReviewCount()['published'] : 0; $saasPendingReviewCount = \array_key_exists('pending', $this->saasStatusReviewCount()) ? $this->saasStatusReviewCount()['pending'] : 0; if ($approveReviewCount != $saasApproveReviewCount) { return \true; } if ($saasPendingReviewCount != $pendingReviewCount) { return \true; } return \false; } public function removeCache() { \delete_transient('rvx_reviews_data_list'); \delete_transient('rvx_review_approve_data'); \delete_transient('rvx_review_pending_data'); \delete_transient('rvx_review_spam_data'); \delete_transient('rvx_review_trash_data'); \delete_transient('rvx_admin_aggregation'); \delete_transient('rvx_review_shortcode'); \delete_transient('rvx_shortcode_transient'); \delete_transient('rvx_shortcode_all_reviews'); } public function clearShortcodesCache($arrayFirst, $arraySecond) { if (empty($arrayFirst)) { return \false; } $firstData = maybe_unserialize($arrayFirst); if (!\is_array($firstData) || !\is_array($arraySecond)) { return \false; } \ksort($firstData); \ksort($arraySecond); $firstHash = \md5(\json_encode($firstData)); $secondHash = \md5(\json_encode($arraySecond)); if ($firstHash === $secondHash) { return \true; } return \false; } /** * Clear product-specific transients for reviews and insight data. * @param int $productId The WP Post ID of the product. */ public function removeProductCache($productId) : void { if (!$productId) { return; } \delete_transient("rvx_{$productId}_latest_reviews"); \delete_transient("rvx_{$productId}_latest_reviews_insight"); } } PK �N,]�.) ) UserSyncService.phpnu ��� <?php namespace Rvx\Services; use Rvx\Utilities\Helper; use Rvx\WPDrill\Facades\DB; class UserSyncService extends \Rvx\Services\Service { protected $users; public function syncUser($file) { $userCount = 0; DB::table('users')->select(['ID', 'display_name', 'user_email', 'user_status'])->chunk(100, function ($allUsers) use($file, &$userCount) { foreach ($allUsers as $user) { $formatedUser = $this->formatUserData($user); Helper::appendToJsonl($file, $formatedUser); $userCount++; } }); Helper::rvxLog($userCount, "User Done"); return $userCount; } public function formatUserData($user) : array { return ['rid' => 'rid://Customer/' . (int) $user->ID, 'wp_id' => (int) $user->ID, 'name' => $user->display_name ?? null, 'email' => is_email($user->user_email) ? $user->user_email : '', 'avatar' => null, 'city' => null, 'phone' => null, 'address' => null, 'country' => null, 'status' => (int) $user->user_status]; } } PK �N,]� Z& & DiscountService.phpnu ��� <?php namespace Rvx\Services; use WC_Coupon; use Exception; use Rvx\Api\DiscountApi; class DiscountService extends \Rvx\Services\Service { public function wpDiscountCreate($data) { $coupon = $this->setBasicCouponData($data); $this->setAdditionalCouponData($coupon, $data); $coupon->save(); return $coupon; } private function setBasicCouponData($data) { $coupon_data = ['code' => sanitize_text_field($data['code']), 'discount_type' => sanitize_text_field($data['discount_type']), 'amount' => sanitize_text_field($data['amount'])]; $coupon = new WC_Coupon(); $coupon->set_props($coupon_data); return $coupon; } private function setAdditionalCouponData($coupon, $data) { if (isset($data['expiry_date'])) { $coupon->set_date_expires(sanitize_text_field($data['expiry_date'])); } if (isset($data['individual_use'])) { $coupon->set_individual_use($data['individual_use'] === 'yes'); } if (isset($data['usage_limit'])) { $coupon->set_usage_limit(sanitize_text_field($data['usage_limit'])); } if (isset($data['free_shipping'])) { $coupon->set_free_shipping($data['free_shipping'] === 'yes'); } if (isset($data['usage_limit_per_user'])) { $coupon->set_usage_limit_per_user((int) $data['usage_limit_per_user']); } if (isset($data['minimum_amount'])) { $coupon->add_meta_data('minimum_amount', sanitize_text_field($data['minimum_amount']), \true); } if (isset($data['maximum_amount'])) { $coupon->add_meta_data('maximum_amount', sanitize_text_field($data['maximum_amount']), \true); } } public function deleteDiscount($couponId) { try { $coupon = new WC_Coupon($couponId); $coupon->delete(\true); // true for force delete return \true; } catch (Exception $e) { return \false; } } public function getDiscount() { return (new DiscountApi())->getDiscount(); } public function discountSetting() { return (new DiscountApi())->discountSetting(); } public function discountSettingsSave($data) { return (new DiscountApi())->discountSettingsSave($data); } public function saveDiscount($data) { return (new DiscountApi())->saveDiscount($data); } public function discountTemplateGet() { return (new DiscountApi())->discountTemplateGet(); } public function discountTemplatePost($data) { return (new DiscountApi())->discountTemplatePost($data); } public function discountMessage($data) { return (new DiscountApi())->discountMessage($data); } } PK �N,]���U U CategoryService.phpnu ��� PK �N,](��' ' � ProductService.phpnu ��� PK �N,]�Z��� � PingService.phpnu ��� PK �N,]�G���) �) 8 ProductSyncService.phpnu ��� PK �N,]�)h� � tF OrderItemSyncService.phpnu ��� PK �N,]�.K �b UserServices.phpnu ��� PK �N,]ݤ��� � �f Api/LoginService.phpnu ��� PK �N,]�֙7 �7 m ImportExportServices.phpnu ��� PK �N,]�2 2 � Service.phpnu ��� PK �N,]��� � a� ReviewService.phpnu ��� PK �N,]$��D D �X EmailService.phpnu ��� PK �N,]vl7�0 �0 a SettingService.phpnu ��� PK �N,]�X��_ _ �� DashboardServices.phpnu ��� PK �N,]��(�* * �� DataSyncService.phpnu ��� PK �N,]n�fR� � � DiscountSyncService.phpnu ��� PK �N,]ɢ�8� � �� OrderService.phpnu ��� PK �N,]��< < �� CptService.phpnu ��� PK �N,]eܠ�� � 4� CategorySyncService.phpnu ��� PK �N,]YX��� � e� GoogleReviewService.phpnu ��� PK �N,]���M �M �� ReviewSyncService.phpnu ��� PK �N,]7W��m m �B CacheServices.phpnu ��� PK �N,]�.) ) 7O UserSyncService.phpnu ��� PK �N,]� Z& & �S DiscountService.phpnu ��� PK s _
| ver. 1.6 |
Github
|
.
| PHP 8.1.34 | Генерация страницы: 0 |
proxy
|
phpinfo
|
Настройка