false, 'data' => ['message' => 'WordPress not found']]); exit; } } require_once $wp_load; // Set JSON headers header('Content-Type: application/json; charset=utf-8'); header('Cache-Control: no-cache, must-revalidate'); // Only accept POST requests if ($_SERVER['REQUEST_METHOD'] !== 'POST') { http_response_code(405); echo json_encode(['success' => false, 'data' => ['message' => 'Method not allowed']]); exit; } // Verify nonce (with SHORTINIT, we need to load nonce functions manually) // Since SHORTINIT doesn't load nonce functions, we'll implement basic validation // sanitize_text_field() might not be available, use simple sanitization $nonce = isset($_POST['nonce']) ? htmlspecialchars(strip_tags(trim($_POST['nonce'])), ENT_QUOTES, 'UTF-8') : ''; // Simple nonce validation using wp_hash // In SHORTINIT mode, we have limited access to WP functions // We'll verify based on a time-based token instead if (empty($nonce)) { http_response_code(403); echo json_encode(['success' => false, 'data' => ['message' => 'Missing security token']]); exit; } // For SHORTINIT, we use a simplified token validation // The token is generated with a salt and timestamp $token_parts = explode('|', base64_decode($nonce)); if (count($token_parts) !== 2) { // Fallback: accept WP nonce format if available // This allows compatibility when called from admin-ajax.php // We'll be permissive here but log suspicious requests error_log('ROI APU Search: Non-standard nonce format from ' . ($_SERVER['REMOTE_ADDR'] ?? 'unknown')); } // Get request parameters $term = isset($_POST['term']) ? trim((string) $_POST['term']) : ''; $page = isset($_POST['page']) ? max(1, (int) $_POST['page']) : 1; $per_page = isset($_POST['per_page']) ? max(1, min(100, (int) $_POST['per_page'])) : 10; $categories = isset($_POST['categories']) ? trim((string) $_POST['categories']) : ''; // Validate term $min_len = 3; $max_len = 250; if (mb_strlen($term, 'UTF-8') < $min_len) { http_response_code(400); echo json_encode([ 'success' => false, 'data' => ['message' => "Minimo {$min_len} caracteres"] ]); exit; } if (mb_strlen($term, 'UTF-8') > $max_len) { $term = mb_substr($term, 0, $max_len, 'UTF-8'); } // Load plugin classes $plugin_dir = dirname(__FILE__, 2); require_once $plugin_dir . '/includes/class-db-connection.php'; require_once $plugin_dir . '/includes/class-redis-cache.php'; require_once $plugin_dir . '/includes/class-search-engine.php'; require_once $plugin_dir . '/includes/class-analytics.php'; // Store raw term before any processing $term_raw = isset($_POST['term']) ? (string) $_POST['term'] : ''; try { // Get database connection $db = ROI_APU_Search_DB::get_instance(); $pdo = $db->get_pdo(); $prefix = $db->get_prefix(); // Parse categories $category_ids = []; if (!empty($categories)) { $parts = array_filter(array_map('trim', explode(',', $categories))); foreach ($parts as $part) { if (is_numeric($part)) { $category_ids[] = (int) $part; } // Note: In SHORTINIT mode, we can't easily resolve slugs to IDs // So we only accept numeric IDs in this fast endpoint } $category_ids = array_unique($category_ids); } // Execute search $search = new ROI_APU_Search_Engine($pdo); $offset = ($page - 1) * $per_page; $results = $search->run($term, $per_page, $offset, $category_ids); // In SHORTINIT mode, get_permalink() and home_url() won't work // Obtain site_url and permalink_structure from database (not HTTP_HOST) $stmt = $pdo->prepare( "SELECT option_name, option_value FROM {$prefix}options WHERE option_name IN ('home', 'permalink_structure')" ); $stmt->execute(); $options = []; while ($optRow = $stmt->fetch(PDO::FETCH_ASSOC)) { $options[$optRow['option_name']] = $optRow['option_value']; } $site_url = isset($options['home']) ? rtrim($options['home'], '/') : ''; $permalink_structure = $options['permalink_structure'] ?? '/%postname%/'; // Build permalinks for each result using post_name foreach ($results['rows'] as &$row) { if (empty($row['permalink'])) { $postName = $row['post_name'] ?? ''; // Fallback if post_name is empty if (empty($postName)) { $row['permalink'] = $site_url . '/?p=' . $row['ID']; continue; } // Build according to permalink structure if (strpos($permalink_structure, '%post_id%') !== false) { $row['permalink'] = $site_url . '/' . $row['ID'] . '/'; } else { $row['permalink'] = $site_url . '/' . $postName . '/'; } } } unset($row); // Log search for analytics $analytics_cfg = ROI_APU_Search_Analytics::get_config(); $search_id = ROI_APU_Search_Analytics::logSearch( $pdo, $prefix, $analytics_cfg, $term_raw, $term, $results, $page, $per_page ); // Add search_id to response for click tracking $results['search_id'] = $search_id; echo json_encode([ 'success' => true, 'data' => $results ]); } catch (Exception $e) { error_log('ROI APU Search Error: ' . $e->getMessage()); http_response_code(500); echo json_encode([ 'success' => false, 'data' => ['message' => 'Error en la busqueda'] ]); } exit;