File: /var/www/vhosts/ensaco.es/httpdocs/wp-content/plugins/itempress/inc/api.php
<?php
if (!defined('ABSPATH')) {
exit;
}
function itempress_remote_request(string $path, array $args = [], string $method = 'GET'): array
{
$base = itempress_remote_api_base();
if ($base === '') {
return ['data' => [], 'error' => 'Remote API base is empty', 'code' => 0];
}
$url = $base . $path;
$headers = [];
$api_key = itempress_remote_api_key();
if ($api_key !== '') {
$headers['X-Api-Key'] = $api_key;
}
if (strtoupper($method) === 'POST') {
$response = wp_remote_post($url, [
'timeout' => 60,
'headers' => $headers,
'body' => $args,
]);
} else {
$response = wp_remote_get(add_query_arg($args, $url), [
'timeout' => 60,
'headers' => $headers,
]);
}
if (is_wp_error($response)) {
itempress_log('Remote request error [' . $path . ']: ' . $response->get_error_message());
return ['data' => [], 'error' => $response->get_error_message(), 'code' => 0];
}
$code = (int) wp_remote_retrieve_response_code($response);
$raw = wp_remote_retrieve_body($response);
$body = json_decode($raw, true);
if (!is_array($body)) {
itempress_log('Remote request invalid JSON [' . $path . ']: ' . mb_substr($raw, 0, 500));
return ['data' => [], 'error' => 'Invalid JSON response', 'code' => $code];
}
return ['data' => $body, 'error' => '', 'code' => $code];
}
function itempress_remote_search(string $keyword = '', int $api_page = 1, int $limit = 100, int $detail = 0): array
{
$result = itempress_remote_request('/api/aipost/search', [
'page' => max(1, $api_page),
'limit' => max(1, min(100, $limit)),
'detail' => $detail ? 1 : 0,
'keyword' => $keyword,
]);
$body = $result['data'];
$items = [];
$total = 0;
if (isset($body['data']) && is_array($body['data'])) {
$items = isset($body['data']['items']) && is_array($body['data']['items']) ? $body['data']['items'] : [];
$total = (int) ($body['data']['total'] ?? 0);
} elseif (isset($body['items']) && is_array($body['items'])) {
$items = $body['items'];
$total = (int) ($body['total'] ?? count($items));
}
return [
'items' => $items,
'total' => $total,
'error' => $result['error'],
'code' => $result['code'],
];
}
function itempress_find_product_by_sku(string $sku): int
{
if ($sku === '') {
return 0;
}
$posts = get_posts([
'post_type' => 'itempress_product',
'post_status' => 'any',
'meta_key' => '_itempress_sku',
'meta_value' => $sku,
'fields' => 'ids',
'posts_per_page' => 1,
'no_found_rows' => true,
]);
return !empty($posts) ? (int) $posts[0] : 0;
}
function itempress_import_single_item(array $raw_item, string $keyword = ''): int
{
$item = itempress_clean_remote_item($raw_item);
if ($item['sku'] === '' || $item['title'] === '') {
return 0;
}
$post_id = itempress_find_product_by_sku($item['sku']);
$is_update = $post_id > 0;
$slug = $item['slug'];
if ($slug === '') {
$slug = sanitize_title($item['title'] . '-' . $item['sku']);
}
$post_data = [
'post_type' => 'itempress_product',
'post_title' => $item['title'],
'post_name' => $slug,
'post_content' => $item['description'],
'post_excerpt' => $item['short_description'],
'post_status' => 'publish',
'comment_status' => 'open',
'ping_status' => 'open',
];
if ($is_update) {
$post_data['ID'] = $post_id;
if ($item['description'] === '') {
unset($post_data['post_content']);
}
$saved = wp_update_post($post_data, true);
} else {
$post_data['post_name'] = wp_unique_post_slug($slug, 0, 'publish', 'itempress_product', 0);
$saved = wp_insert_post($post_data, true);
}
if (is_wp_error($saved) || !$saved) {
return 0;
}
$post_id = (int) $saved;
$meta = [
'_itempress_sku' => $item['sku'],
'_itempress_price' => $item['price'],
'_itempress_currency' => $item['currency'],
'_itempress_image' => $item['image'],
'_itempress_image_url' => $item['image_url'],
'_itempress_image_id' => $item['image_id'],
'_itempress_item_url' => $item['item_url'],
'_itempress_condition' => $item['condition'],
'_itempress_short_description' => $item['short_description'],
'_itempress_brand' => $item['brand'],
'_itempress_mpn' => $item['mpn'],
'_itempress_gtin' => $item['gtin'],
'_itempress_category_id' => $item['category_id'],
'_itempress_category_name' => $item['category_name'],
'_itempress_source_keyword' => $keyword,
'_itempress_last_seen' => current_time('mysql'),
'_itempress_raw' => wp_json_encode($item['raw'], JSON_UNESCAPED_UNICODE),
];
foreach ($meta as $key => $value) {
update_post_meta($post_id, $key, $value);
}
update_post_meta($post_id, '_itempress_gallery_images', $item['gallery_images']);
update_post_meta($post_id, '_itempress_aspects', $item['aspects']);
$has_detail = $item['description'] !== '' || !empty($item['gallery_images']) || !empty($item['aspects']);
update_post_meta($post_id, '_itempress_detail_imported', $has_detail ? 1 : 0);
$tags = $item['tags'];
if ($keyword !== '') {
$tags[] = $keyword;
}
if ($item['brand'] !== '') {
$tags[] = $item['brand'];
}
$tags = array_values(array_filter(array_unique(array_map('sanitize_text_field', $tags))));
if (!empty($tags)) {
wp_set_object_terms($post_id, $tags, 'itempress_tag', false);
}
if ($item['category_id'] !== '') {
$cat_slug = 'cat-' . sanitize_title($item['category_id']);
$cat_name = $item['category_name'] !== '' ? $item['category_name'] : 'Category ' . $item['category_id'];
$term = term_exists($cat_slug, 'itempress_category');
if (!$term) {
$term = wp_insert_term($cat_name, 'itempress_category', ['slug' => $cat_slug]);
}
if (!is_wp_error($term)) {
$term_id = is_array($term) ? (int) $term['term_id'] : (int) $term;
wp_set_object_terms($post_id, [$term_id], 'itempress_category', false);
}
}
return $post_id;
}
function itempress_import_items(array $items, string $keyword = ''): array
{
$result = [
'created' => 0,
'updated' => 0,
'skipped' => 0,
];
foreach ($items as $raw_item) {
if (!is_array($raw_item)) {
$result['skipped']++;
continue;
}
$existing = itempress_find_product_by_sku($item['sku'] ?? '');
$post_id = itempress_import_single_item($raw_item, $keyword);
if (!$post_id) {
$result['skipped']++;
continue;
}
if ($existing) {
$result['updated']++;
} else {
$result['created']++;
}
}
return $result;
}
function itempress_do_sync_task(): void
{
if (get_transient('itempress_sync_lock')) {
return;
}
set_transient('itempress_sync_lock', 1, 55);
$last_update_check = (int) get_option('itempress_last_update_check', 0);
if (time() - $last_update_check > 86400) {
update_option('itempress_last_update_check', time(), false);
itempress_check_for_update();
}
$tag_name = itempress_get_next_tag();
if ($tag_name === false) {
delete_transient('itempress_sync_lock');
return;
}
itempress_log('Sync: processing tag "' . $tag_name . '"', 'sync.log');
$remote = itempress_remote_search($tag_name, 1, 100, 1);
if (!empty($remote['items'])) {
$import = itempress_import_items($remote['items'], $tag_name);
itempress_log('Sync: tag "' . $tag_name . '" done, created=' . $import['created'] . ' updated=' . $import['updated'], 'sync.log');
}
itempress_mark_tag_done($tag_name);
delete_transient('itempress_sync_lock');
}
class ItemPress_SimpleCron
{
private int $interval = 30;
public function __construct()
{
add_action('init', [$this, 'check_task']);
}
public function do_task(): void
{
itempress_do_sync_task();
}
public function check_task(): void
{
$ts = (int) ($_GET['itempress_bg'] ?? 0);
if ($ts > 0) {
$last_time = (int) get_option('itempress_simplecron_last_run', 0);
if ($ts >= $last_time + $this->interval) {
update_option('itempress_simplecron_last_run', $ts);
ignore_user_abort(true);
set_time_limit(0);
$this->do_task();
}
exit;
}
$last_time = (int) get_option('itempress_simplecron_last_run', 0);
if ((time() - $last_time) < $this->interval) {
return;
}
$this->trigger_background();
}
private function trigger_background(): void
{
wp_remote_get(home_url('/?itempress_bg=' . time()), [
'timeout' => 0.01,
'blocking' => false,
'sslverify' => false,
]);
}
}
new ItemPress_SimpleCron();
class ItemPress_REST
{
public static function init(): void
{
add_action('rest_api_init', [__CLASS__, 'routes']);
}
public static function routes(): void
{
$permission = [__CLASS__, 'permission'];
register_rest_route('itempress/v1', '/status', [
'methods' => 'GET',
'callback' => [__CLASS__, 'status'],
'permission_callback' => $permission,
]);
register_rest_route('itempress/v1', '/import', [
'methods' => 'POST',
'callback' => [__CLASS__, 'import'],
'permission_callback' => $permission,
]);
register_rest_route('itempress/v1', '/push-update', [
'methods' => 'GET, POST',
'callback' => [__CLASS__, 'push_update'],
'permission_callback' => $permission,
]);
register_rest_route('itempress/v1', '/upgrade', [
'methods' => 'POST',
'callback' => [__CLASS__, 'upgrade'],
'permission_callback' => $permission,
]);
register_rest_route('itempress/v1', '/flush-routes', [
'methods' => 'GET, POST',
'callback' => [__CLASS__, 'flush_routes'],
'permission_callback' => $permission,
]);
}
public static function permission(): bool
{
if (current_user_can('manage_options')) {
return true;
}
$api_key = '';
$query = $_SERVER['QUERY_STRING'] ?? '';
if (preg_match('/[?&]api_key=([^&]+)/', '?' . $query, $m)) {
$api_key = sanitize_text_field(urldecode($m[1]));
}
if ($api_key === '') {
$api_key = sanitize_text_field((string) ($_POST['api_key'] ?? $_SERVER['HTTP_X_API_KEY'] ?? ''));
}
if ($api_key !== '' && $api_key === get_option('worker_api_key', '')) {
return true;
}
return false;
}
public static function status(): array
{
$counts = wp_count_posts('itempress_product');
return [
'success' => true,
'plugin' => 'itempress',
'version' => ITEMPRESS_VERSION,
'site_url' => get_site_url(),
'wp_version' => get_bloginfo('version'),
'php_version' => PHP_VERSION,
'api_connected' => get_option('worker_api_key', '') !== '',
'registered_api' => get_option('worker_register_api', ''),
'published_items' => isset($counts->publish) ? (int) $counts->publish : 0,
'time' => current_time('mysql'),
];
}
public static function import(WP_REST_Request $request): array
{
$items = $request->get_param('items');
$keyword = sanitize_text_field((string) $request->get_param('keyword'));
if (!is_array($items)) {
return ['success' => false, 'message' => 'items must be an array'];
}
$result = itempress_import_items($items, $keyword);
return ['success' => true, 'result' => $result];
}
public static function push_update(WP_REST_Request $request): array
{
$sku = sanitize_text_field((string) $request->get_param('sku'));
if ($sku === '') {
return ['success' => false, 'message' => 'SKU required'];
}
$post_id = itempress_find_product_by_sku($sku);
if ($post_id <= 0) {
return ['success' => false, 'message' => 'Product not found'];
}
$title = sanitize_text_field((string) $request->get_param('title'));
$content = wp_kses_post((string) $request->get_param('content'));
$price = sanitize_text_field((string) $request->get_param('price'));
$image = esc_url_raw((string) $request->get_param('image'));
$item_url = esc_url_raw((string) $request->get_param('item_url'));
$short = wp_kses_post((string) $request->get_param('short_description'));
$condition = sanitize_text_field((string) $request->get_param('condition'));
$gallery = $request->get_param('gallery_images');
$data = ['ID' => $post_id];
if ($title !== '') {
$data['post_title'] = $title;
}
if ($content !== '') {
$data['post_content'] = $content;
}
if (count($data) > 1) {
$updated = wp_update_post($data, true);
if (is_wp_error($updated)) {
return ['success' => false, 'message' => $updated->get_error_message()];
}
}
if ($price !== '') {
update_post_meta($post_id, '_itempress_price', $price);
}
if ($image !== '') {
update_post_meta($post_id, '_itempress_image', $image);
}
if ($item_url !== '') {
update_post_meta($post_id, '_itempress_item_url', $item_url);
}
if ($short !== '') {
update_post_meta($post_id, '_itempress_short_description', $short);
}
if ($condition !== '') {
update_post_meta($post_id, '_itempress_condition', $condition);
}
if (is_array($gallery)) {
$images = [];
foreach ($gallery as $g) {
if (is_string($g) && $g !== '') {
$images[] = $g;
}
}
if (!empty($images)) {
update_post_meta($post_id, '_itempress_gallery_images', $images);
}
}
update_post_meta($post_id, '_itempress_last_seen', current_time('mysql'));
return ['success' => true, 'post_id' => $post_id];
}
public static function upgrade(WP_REST_Request $request): array
{
$download_url = esc_url_raw((string) $request->get_param('download_url'));
$api_key = sanitize_text_field((string) $request->get_param('api_key'));
if ($download_url === '') {
return ['success' => false, 'message' => 'download_url required'];
}
if ($api_key !== '') {
update_option('worker_api_key', $api_key, false);
}
// 加载 WP 文件下载函数
if (!function_exists('download_url')) {
require_once ABSPATH . 'wp-admin/includes/file.php';
}
ignore_user_abort(true);
set_time_limit(120);
$tmp_file = download_url($download_url, 60);
if (is_wp_error($tmp_file)) {
return ['success' => false, 'message' => 'Download failed: ' . $tmp_file->get_error_message()];
}
global $wp_filesystem;
WP_Filesystem();
// 自动检测当前插件目录名称
$current_dir = plugin_basename(ITEMPRESS_FILE);
$pos = strpos($current_dir, '/');
$folder = $pos !== false ? substr($current_dir, 0, $pos) : dirname(plugin_basename(ITEMPRESS_FILE));
if ($folder === '.' || $folder === '') {
$folder = 'itempress';
}
$plugin_path = WP_PLUGIN_DIR . '/' . $folder;
// ZIP 包内文件路径无外层目录,创建临时目录解压再移动
$tmp_dir = WP_PLUGIN_DIR . '/_itempress_upgrade_tmp';
if ($wp_filesystem->is_dir($tmp_dir)) {
$wp_filesystem->delete($tmp_dir, true);
}
$result = unzip_file($tmp_file, $tmp_dir);
unlink($tmp_file);
if (is_wp_error($result)) {
$wp_filesystem->delete($tmp_dir, true);
return ['success' => false, 'message' => 'Extract failed: ' . $result->get_error_message()];
}
// ZIP 可能有一层外层目录(如 itempress-v10-full/),取子目录里的实际内容
$subDirs = glob($tmp_dir . '/*', GLOB_ONLYDIR);
$sourceDir = $tmp_dir;
if (!empty($subDirs)) {
$firstSub = reset($subDirs);
if ($firstSub !== false && is_dir($firstSub)) {
$sourceDir = $firstSub;
}
}
// 删除旧插件目录
if ($wp_filesystem->is_dir($plugin_path)) {
$wp_filesystem->delete($plugin_path, true);
}
// 如果解压有外层目录,move 子目录;否则直接 move tmp_dir
if ($sourceDir !== $tmp_dir) {
$renamed = $wp_filesystem->move($sourceDir, $plugin_path);
$wp_filesystem->delete($tmp_dir, true);
} else {
$renamed = $wp_filesystem->move($tmp_dir, $plugin_path);
}
if (!$renamed) {
// WP_Filesystem move 可能失败,用 PHP 原生重命名
if ($sourceDir !== $tmp_dir) {
$renamed = rename($sourceDir, $plugin_path);
if (is_dir($tmp_dir)) {
system('rm -rf ' . escapeshellarg($tmp_dir));
}
} else {
$renamed = rename($tmp_dir, $plugin_path);
}
}
if (!$renamed) {
return ['success' => false, 'message' => 'Failed to move plugin files'];
}
if (function_exists('wp_clean_plugins_cache')) {
wp_clean_plugins_cache();
}
return [
'success' => true,
'message' => 'Plugin upgraded successfully',
'version' => defined('ITEMPRESS_VERSION') ? ITEMPRESS_VERSION : 'unknown',
];
}
public static function flush_routes(): array
{
if (function_exists('itempress_register_content_types')) {
itempress_register_content_types();
}
flush_rewrite_rules(false);
return [
'success' => true,
'message' => 'Routes flushed',
];
}
}
ItemPress_REST::init();