// =================================================================== // === ROUTER (V12) - CON SOPORTE PARA TAGS Y HISTÓRICO === // =================================================================== // 1. SISTEMA DE CACHÉ HTML (SIMPLE) if ($_SERVER['REQUEST_METHOD'] === 'GET' && !isset($_GET['q'])) { $req_slug = trim($_GET['slug'] ?? '', '/'); if (empty($req_slug)) $req_slug = 'usd'; // Solo cacheamos páginas "estándar" (Portada, Divisas). // Evitamos cachear búsquedas, tags o URLs complejas aquí para no llenar disco. if (preg_match('/^[a-z0-9-]+$/', $req_slug) && !in_array($req_slug, ['news', 'contact', 'search', 'dolar-historico', 'tag'])) { $cache_file = __DIR__ . '/cache/html/' . ($req_slug === 'usd' ? 'index' : $req_slug) . '.html'; if (file_exists($cache_file) && (time() - filemtime($cache_file) < 60)) { readfile($cache_file); exit; } } } // 2. CONFIGURACIÓN INICIAL header('Content-Type: text/html; charset=utf-8'); date_default_timezone_set('America/Santiago'); ob_start(); // Iniciar buffer de salida ini_set('display_errors', 0); $config = require __DIR__ . '/includes/site-config.php'; require_once __DIR__ . '/includes/functions.php'; // Obtener el slug solicitado $slug = trim($_GET['slug'] ?? '', '/'); if (empty($slug)) $slug = 'usd'; $slug_parts = explode('/', $slug); // Cargar datos de monedas $currency_pages = require __DIR__ . '/includes/currency-data.php'; $page_config = null; $page_data = []; // =================================================================== // === ENRUTADOR MAESTRO === // =================================================================== // A. PÁGINAS DE DIVISAS (USD, EUR, UF, ETC.) if (isset($currency_pages[$slug])) { $page_config = $currency_pages[$slug]; $page_config['current_nav'] = $slug; // Para resaltar en menú $page_config['template'] = 'currency-converter-content.php'; require_once __DIR__ . '/includes/database.php'; // Limpieza de seguridad para nombre de tabla $clean_code = preg_replace('/[^a-z0-9_]/', '', $page_config['db_slug'] ?? $slug); $table = "valor_dolar_{$clean_code}"; $current_rate = 0; $db_timestamp = null; $history_data = []; if ($conn) { // Traemos los últimos 10 registros para calcular variaciones (ayer, semana) $res = $conn->query("SELECT valor, timestamp, fecha FROM {$table} ORDER BY timestamp DESC LIMIT 10"); if ($res && $res->num_rows > 0) { $rows = $res->fetch_all(MYSQLI_ASSOC); // El primero es el actual $current_rate = (float)$rows[0]['valor']; $db_timestamp = $rows[0]['timestamp']; // Guardamos todo para cálculos de tendencia foreach($rows as $r) { $history_data[] = (float)$r['valor']; } } // FIX DE SEGURIDAD: Si la DB falló y el precio es 0, intentar leer caché de disco if ($current_rate <= 0) { $json_cache = __DIR__ . "/cache/prices/price_{$clean_code}.json"; if (file_exists($json_cache)) { $json_data = json_decode(file_get_contents($json_cache), true); if (isset($json_data['price'])) { $current_rate = (float)$json_data['price']; } } } } $page_data = [ 'rate' => $current_rate, 'db_timestamp' => $db_timestamp, 'history' => $history_data, 'currency_code' => $clean_code, 'from_currency' => [ 'name' => $page_config['currency_name'], 'singular' => $page_config['currency_name_singular'] ?? 'dólar', 'plural' => $page_config['currency_name_plural'] ?? 'dólares', 'code' => strtoupper($page_config['iso_code'] ?? 'USD'), 'flag' => $page_config['currency_flag'] ?? 'usa.png' ] ]; } // B. SECCIÓN DE NOTICIAS (CON FIX DE PAGINACIÓN) elseif ($slug_parts[0] === 'news') { $wp_api = $config['base_url'] . '/blog/wp-json/wp/v2'; // B.1 Noticia Individual (Si hay slug y NO es "page") // Ejemplo: /news/titulo-noticia if (isset($slug_parts[1]) && $slug_parts[1] !== 'page') { $post_slug = htmlspecialchars($slug_parts[1]); $resp = fetch_from_api_with_cache("{$wp_api}/posts?slug={$post_slug}&_embed", 3600); $post = $resp['body'][0] ?? null; if ($post) { // Limpieza y Corte Inteligente para SEO $raw_excerpt = strip_tags($post['excerpt']['rendered']); $raw_excerpt = str_replace(["\r", "\n", " "], " ", $raw_excerpt); $raw_excerpt = html_entity_decode($raw_excerpt); $raw_excerpt = trim($raw_excerpt); $limit = 155; if (mb_strlen($raw_excerpt) > $limit) { $sub = mb_substr($raw_excerpt, 0, $limit + 5); $last_space = mb_strrpos($sub, ' '); $meta_desc = ($last_space !== false) ? mb_substr($sub, 0, $last_space) . '...' : mb_substr($raw_excerpt, 0, $limit) . '...'; } else { $meta_desc = $raw_excerpt; } $feat_media = $post['_embedded']['wp:featuredmedia'][0] ?? null; $img_alt = $feat_media['alt_text'] ?? $post['title']['rendered']; if(empty($img_alt)) $img_alt = "Noticia sobre " . $post['title']['rendered']; $page_config = [ 'template' => 'single-post-content.php', 'current_nav' => 'news', 'post_data' => $post, 'page_title' => $post['title']['rendered'], 'description' => $meta_desc, 'og_image' => $post['_embedded']['wp:featuredmedia'][0]['source_url'] ?? null, 'og_image_alt' => $img_alt ]; } else { $page_config = ['template' => '404-content.php', 'page_title' => 'Noticia no encontrada']; } } // B.2 Archivo de Noticias (Lista con Paginación) // Ejemplo: /news o /news/page/2 else { $current_page = 1; // Caso 1: URL tipo /news?page=2 if (isset($_GET['page'])) { $current_page = (int)$_GET['page']; } // Caso 2: URL tipo /news/page/2 elseif (isset($slug_parts[1]) && $slug_parts[1] === 'page' && isset($slug_parts[2])) { $current_page = (int)$slug_parts[2]; } if ($current_page < 1) $current_page = 1; $page_config = [ 'template' => 'archive-content.php', 'current_nav' => 'news', 'type' => 'news_archive', 'breadcrumbs' => [['text'=>'Noticias']], 'api_endpoint_count' => "{$wp_api}/posts", 'api_endpoint_posts' => "{$wp_api}/posts?_embed", 'base_pagination_url' => "/news", 'h1_title' => "Noticias Financieras y Económicas", 'page_title' => "Noticias del Dólar y Economía en Chile | Valor Dólar - Pág $current_page", 'description' => "Lee las últimas noticias sobre el tipo de cambio, economía chilena y mercado internacional.", 'current_page' => $current_page ]; } } // G. SECCIÓN DE CATEGORÍAS elseif ($slug_parts[0] === 'category' && isset($slug_parts[1])) { $cat_slug = htmlspecialchars($slug_parts[1]); $current_page = 1; // --- FIX: DETECTAR PÁGINA EN URL (/category/slug/page/2) --- if (isset($slug_parts[2]) && $slug_parts[2] === 'page' && isset($slug_parts[3])) { $current_page = (int)$slug_parts[3]; } elseif (isset($_GET['page'])) { $current_page = (int)$_GET['page']; } if ($current_page < 1) $current_page = 1; $wp_api = $config['base_url'] . '/blog/wp-json/wp/v2'; // 1. Obtener ID de la categoría (Cache 1 hora) $cat_resp = fetch_from_api_with_cache("{$wp_api}/categories?slug={$cat_slug}", 3600); $category_info = $cat_resp['body'][0] ?? null; if ($category_info) { $cat_id = $category_info['id']; $cat_name = $category_info['name']; $page_config = [ 'template' => 'archive-content.php', 'current_nav' => 'news', 'type' => 'category_archive', 'breadcrumbs' => [ ['text'=>'Noticias', 'link'=>'/news'], ['text'=>$cat_name] ], // Endpoints filtrados por categoría 'api_endpoint_count' => "{$wp_api}/posts?categories={$cat_id}", 'api_endpoint_posts' => "{$wp_api}/posts?_embed&categories={$cat_id}", 'base_pagination_url' => "/category/{$cat_slug}", 'h1_title' => "Noticias de: " . $cat_name, 'page_title' => "{$cat_name} - Página {$current_page} | Valor Dólar", 'description' => "Lee las últimas noticias sobre {$cat_name} en Chile y el mundo.", 'current_page' => $current_page ]; } else { $page_config = ['template' => '404-content.php', 'page_title' => 'Categoría no encontrada']; } } // =================================================================== // I. SECCIÓN DE ETIQUETAS (TAGS) - CON FALLBACK INTELIGENTE // =================================================================== elseif ($slug_parts[0] === 'tag' && isset($slug_parts[1])) { $tag_slug = htmlspecialchars($slug_parts[1]); $current_page = 1; if (isset($slug_parts[2]) && $slug_parts[2] === 'page' && isset($slug_parts[3])) { $current_page = (int)$slug_parts[3]; } elseif (isset($_GET['page'])) { $current_page = (int)$_GET['page']; } if ($current_page < 1) $current_page = 1; $wp_api = $config['base_url'] . '/blog/wp-json/wp/v2'; // 1. Consultar si el TAG existe realmente en WordPress $tag_resp = fetch_from_api_with_cache("{$wp_api}/tags?slug={$tag_slug}", 3600); $tag_info = $tag_resp['body'][0] ?? null; if ($tag_info) { // --- CASO A: EL TAG SÍ EXISTE --- $tag_id = $tag_info['id']; $tag_name = $tag_info['name']; $page_config = [ 'template' => 'archive-content.php', 'current_nav' => 'news', 'type' => 'tag_archive', 'breadcrumbs' => [ ['text'=>'Noticias', 'link'=>'/news'], ['text'=>'Etiqueta: ' . $tag_name] ], 'api_endpoint_count' => "{$wp_api}/posts?tags={$tag_id}", 'api_endpoint_posts' => "{$wp_api}/posts?_embed&tags={$tag_id}", 'base_pagination_url' => "/tag/{$tag_slug}", 'h1_title' => "Noticias sobre: " . $tag_name, 'page_title' => "{$tag_name} (Etiqueta) - Pág {$current_page} | Valor Dólar", 'description' => "Recopilación de noticias y análisis relacionados con {$tag_name}.", 'current_page' => $current_page ]; } else { // --- CASO B: EL TAG NO EXISTE (FALLBACK MÁGICO) --- // En lugar de dar error 404, redirigimos al buscador. // Ejemplo: /tag/noticias -> /search?q=noticias // Limpiamos el slug (quitamos guiones) para buscar mejor $clean_query = str_replace('-', ' ', $tag_slug); header("Location: " . $config['base_url'] . "/search?q=" . urlencode($clean_query), true, 301); exit; } } // C. SECCIÓN HISTÓRICA - PORTADA elseif ($slug === 'dolar-historico') { require_once __DIR__ . '/includes/database.php'; $years = []; if($conn) { $res = $conn->query("SELECT DISTINCT YEAR(fecha) as anio FROM valor_dolar_obs ORDER BY anio DESC"); while($r = $res->fetch_assoc()) $years[] = $r['anio']; } $page_config = [ 'template' => 'historical-archive.php', 'current_nav' => 'dolar-historico', 'page_title' => 'Histórico del Dólar en Chile (1984 - Hoy) | Valor Dólar', 'description' => 'Consulta la base de datos histórica completa del valor del dólar observado en Chile. Tablas anuales, mensuales y calculadora de valores pasados.', 'view_type' => 'main', 'years_list' => $years ]; } // D. SECCIÓN HISTÓRICA - AÑO elseif ($slug === 'historical-year') { require_once __DIR__ . '/includes/database.php'; $year = (int)($_GET['year'] ?? date('Y')); $stats = ['avg_val' => 0, 'max_val' => 0]; if ($conn) { $stmt = $conn->prepare("SELECT AVG(valor) as prom, MAX(valor) as max FROM valor_dolar_obs WHERE YEAR(fecha) = ?"); $stmt->bind_param("i", $year); $stmt->execute(); $res = $stmt->get_result(); if ($row = $res->fetch_assoc()) { $stats['avg_val'] = $row['prom']; $stats['max_val'] = $row['max']; } } $seo_prom = number_format($stats['avg_val'], 0, ',', '.'); $seo_max = number_format($stats['max_val'], 0, ',', '.'); $page_config = [ 'template' => 'historical-archive.php', 'current_nav' => 'dolar-historico', 'page_title' => "Precio del Dólar año $year en Chile: Resumen y Gráfico", 'description' => "Resumen del dólar en el año $year. El precio promedio fue de $$seo_prom y alcanzó un máximo de $$seo_max. Revisa la tabla completa día a día.", 'view_type' => 'year', 'data_year' => $year, 'year_stats' => $stats ]; } // E. SECCIÓN HISTÓRICA - MES elseif ($slug === 'historical-month') { require_once __DIR__ . '/includes/database.php'; $year = (int)($_GET['year'] ?? date('Y')); $month_input = strtolower(trim($_GET['month'] ?? '')); $meses_map = ['enero'=>1, 'febrero'=>2, 'marzo'=>3, 'abril'=>4, 'mayo'=>5, 'junio'=>6, 'julio'=>7, 'agosto'=>8, 'septiembre'=>9, 'octubre'=>10, 'noviembre'=>11, 'diciembre'=>12]; $month_num = is_numeric($month_input) ? (int)$month_input : ($meses_map[$month_input] ?? 0); if ($month_num > 0 && $conn) { $query = "SELECT * FROM valor_dolar_obs WHERE YEAR(fecha) = ? AND MONTH(fecha) = ? ORDER BY fecha ASC"; $stmt = $conn->prepare($query); $stmt->bind_param("ii", $year, $month_num); $stmt->execute(); $res = $stmt->get_result(); $data = []; $min = 99999; $max = 0; $sum = 0; $count = 0; $start_val = 0; $end_val = 0; while($row = $res->fetch_assoc()) { $val = (float)$row['valor']; $data[] = ['date' => $row['fecha'], 'val' => $val]; if ($val < $min) $min = $val; if ($val > $max) $max = $val; $sum += $val; $count++; $end_val = $val; if ($start_val == 0) $start_val = $val; } $stats = ['min' => ($count > 0)?$min:0, 'max' => $max, 'avg' => ($count > 0)?$sum/$count:0, 'start' => $start_val, 'end' => $end_val]; $meses_inv = array_flip($meses_map); $mes_nombre = ucfirst($meses_inv[$month_num] ?? $month_input); $seo_inicio = number_format($stats['start'], 0, ',', '.'); $seo_fin = number_format($stats['end'], 0, ',', '.'); $seo_prom = number_format($stats['avg'], 0, ',', '.'); $page_config = [ 'template' => 'historical-month-report.php', 'current_nav' => 'dolar-historico', 'page_title' => "Valor Dólar $mes_nombre $year - Tabla Oficial SII", 'description' => "En $mes_nombre de $year, el dólar en Chile inició en $$seo_inicio y cerró en $$seo_fin, promediando $$seo_prom. Revisa la variación diaria aquí.", 'data' => $data, 'stats' => $stats, 'report_year' => $year, 'report_month' => $mes_nombre, 'month_num' => $month_num ]; } else { $page_config = null; } } // H. SECCIÓN DE BÚSQUEDA elseif ($slug === 'search') { $query = htmlspecialchars($_GET['q'] ?? ''); $wp_api = $config['base_url'] . '/blog/wp-json/wp/v2'; $current_page = isset($_GET['page']) ? (int)$_GET['page'] : 1; if ($current_page < 1) $current_page = 1; // Si hay búsqueda, consultamos a WP if (!empty($query)) { // 1. Obtener TOTAL de resultados $count_api = "{$wp_api}/posts?search=" . urlencode($query); $ctx = stream_context_create(["http" => ["ignore_errors" => true, "timeout" => 3]]); @file_get_contents($count_api, false, $ctx); $headers = $http_response_header ?? []; $total_pages = 1; foreach ($headers as $header) { if (stripos($header, 'X-WP-TotalPages') !== false) { $total_pages = (int)trim(explode(':', $header)[1]); break; } } // 2. Obtener los posts $search_api = "{$wp_api}/posts?_embed&search=" . urlencode($query) . "&per_page=10&page={$current_page}"; $search_resp = fetch_from_api_with_cache($search_api, 300); $posts = $search_resp['body'] ?? []; $page_config = [ 'template' => 'archive-content.php', 'current_nav' => 'news', 'type' => 'search_results', 'breadcrumbs' => [['text'=>'Búsqueda']], 'h1_title' => "Resultados para: " . $query, 'page_title' => "Búsqueda: {$query} - Pág {$current_page} | Valor Dólar", 'description' => "Resultados de búsqueda para {$query}.", 'manual_posts' => $posts, 'manual_total_pages' => $total_pages, // Pasamos el total calculado 'current_page' => $current_page, 'base_pagination_url' => "/search?q=" . urlencode($query) ]; } else { // Búsqueda vacía $page_config = [ 'template' => 'archive-content.php', 'h1_title' => "Búsqueda", 'page_title' => "Buscar | Valor Dólar", 'manual_posts' => [], 'manual_total_pages' => 1 ]; } } // F. PÁGINAS ESTÁTICAS elseif (in_array($slug, ['contact', 'privacy-policy', 'about-us', 'mapa-del-sitio'])) { $titles = [ 'contact' => 'Contacto', 'privacy-policy' => 'Política de Privacidad', 'about-us' => 'Nosotros', 'mapa-del-sitio' => 'Mapa del Sitio' ]; $template_file = ($slug === 'mapa-del-sitio') ? 'sitemap-html-content.php' : "{$slug}-content.php"; $page_config = [ 'template' => $template_file, 'current_nav' => $slug, 'page_title' => $titles[$slug] . ' | Valor Dólar' ]; } // 404 - PÁGINA NO ENCONTRADA if ($page_config === null) { header("HTTP/1.0 404 Not Found"); $page_config = [ 'template' => '404-content.php', 'page_title' => 'Página no encontrada', 'current_nav' => '' ]; } // =================================================================== // === RENDERIZADO FINAL === // =================================================================== require_once __DIR__ . '/includes/header.php'; $tpl = __DIR__ . '/templates/' . $page_config['template']; if(file_exists($tpl)) { require_once $tpl; } else { echo "
Error: Plantilla no encontrada ($tpl)
"; } require_once __DIR__ . '/includes/footer.php'; // =================================================================== // === GUARDADO DE CACHÉ === // =================================================================== $html = ob_get_contents(); ob_end_flush(); // Solo guardamos caché de páginas estables if ($slug === 'usd' || isset($currency_pages[$slug])) { $cf = __DIR__ . '/cache/html/' . ($slug === 'usd' ? 'index' : $slug) . '.html'; if (!is_dir(dirname($cf))) @mkdir(dirname($cf), 0775, true); file_put_contents($cf, $html, LOCK_EX); } ?>