fix(analytics): Corregir URLs en analytics usando post_name desde BD
Problema: - URLs en analytics mostraban dominio incorrecto (HTTP_HOST) - URLs usaban formato /?p=ID en lugar de permalinks Solución: - class-search-engine.php: Agregar propiedad $prefix, incluir p.post_name en 5 queries fetch, agregar helpers getSiteUrlFromDb(), getPermalinkStructure() y buildPermalink() - search-endpoint.php: Obtener site_url y permalink_structure desde wp_options, construir URLs con post_name - click-endpoint.php: Fallback de dest usando post_name desde BD Archivos modificados: - includes/class-search-engine.php - api/search-endpoint.php - api/click-endpoint.php 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
191
api/search-endpoint.php
Normal file
191
api/search-endpoint.php
Normal file
@@ -0,0 +1,191 @@
|
||||
<?php
|
||||
/**
|
||||
* Fast AJAX Search Endpoint with SHORTINIT bypass
|
||||
*
|
||||
* This endpoint loads minimal WordPress for maximum speed.
|
||||
* Use this directly via plugin URL for fastest results.
|
||||
*
|
||||
* @package ROI_APU_Search
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
// BYPASS: Don't load full WordPress
|
||||
define('SHORTINIT', true);
|
||||
define('WP_USE_THEMES', false);
|
||||
|
||||
// Find wp-load.php (go up from plugin directory)
|
||||
$wp_load = dirname(__FILE__, 5) . '/wp-load.php';
|
||||
|
||||
if (!file_exists($wp_load)) {
|
||||
// Fallback: try alternative paths
|
||||
$wp_load = dirname(__FILE__, 6) . '/wp-load.php';
|
||||
if (!file_exists($wp_load)) {
|
||||
http_response_code(500);
|
||||
echo json_encode(['success' => 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;
|
||||
Reference in New Issue
Block a user