Files
roi-theme/buscar-apus/index.php
root a22573bf0b Commit inicial - WordPress Análisis de Precios Unitarios
- WordPress core y plugins
- Tema Twenty Twenty-Four configurado
- Plugin allow-unfiltered-html.php simplificado
- .gitignore configurado para excluir wp-config.php y uploads

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-03 21:04:30 -06:00

310 lines
12 KiB
PHP
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
// public_html/buscar-apus/index.php
$cfg = require __DIR__ . '/config.php';
require __DIR__ . '/app/Db.php';
require __DIR__ . '/app/Logger.php';
require __DIR__ . '/app/Search.php';
require __DIR__ . '/menu.php';
require __DIR__ . '/app/Analytics.php';
if (session_status() !== PHP_SESSION_ACTIVE) @session_start();
Logger::log($cfg['log'], 'peticion', [
'uri' => $_SERVER['REQUEST_URI'] ?? '',
'ua' => $_SERVER['HTTP_USER_AGENT'] ?? '',
'get' => $_GET
]);
/**
* =========================
* Config de anuncios
* =========================
* - $ADS_BETWEEN_MAX: máximo de anuncios entre resultados.
* - $ALLOWED_AD_UNITS: tipos permitidos para los anuncios intermedios.
* - $TOP_FIXED_AD_SIZE: tamaño fijo del anuncio bajo el buscador ('300x250' | '728x90' | '320x100' | '970x250').
* - $TOP_FIXED_AD_SLOT: SLOT de AdSense para ese tamaño fijo (reemplázalo por el tuyo).
*/
$ADS_BETWEEN_MAX = 1; // 1..n
$ALLOWED_AD_UNITS = ['auto', 'autorelaxed', 'in-article'];
$TOP_FIXED_AD_SIZE = '1100x315';
$TOP_FIXED_AD_SLOT = '2873062302'; // <-- pon aquí tu data-ad-slot (numérico)
// Parámetros
$raw = isset($_GET['s']) ? trim((string)$_GET['s']) : '';
$page = (isset($_GET['pagina']) && ctype_digit($_GET['pagina']) && (int)$_GET['pagina'] > 0) ? (int)$_GET['pagina'] : 1;
$pp = max(1, min(100, (int)$cfg['limits']['per_page']));
$off = ($page - 1) * $pp;
[$valid, $term, $err] = Search::sanitizeTerm($raw, $cfg['limits']['min_len'], $cfg['limits']['max_len']);
$results = ['total'=>0,'rows'=>[],'modo'=>'NONE','time_ms'=>0];
$pdo = null; $searchId = null;
if ($valid) {
$pdo = Db::pdo($cfg);
$search = new Search($pdo, $cfg['db']['prefix'], $cfg['log']);
$results = $search->run($term, $pp, $off);
$analyticsCfg = $cfg['analytics'] ?? ['enabled' => false];
// al terminar la búsqueda:
$searchId = Analytics::logSearch(
$pdo, $cfg['db']['prefix'], $analyticsCfg,
$raw, $term, $results, $page, $pp
);
} else if ($raw !== '') {
Logger::log($cfg['log'], 'consulta_invalida', ['error' => $err]);
}
/** ===== Helpers ===== **/
// Resalta el texto (sin snippet). Útil para títulos.
function apu_highlight(string $text, string $term): string {
$safe = htmlspecialchars($text, ENT_QUOTES, 'UTF-8');
foreach (array_filter(explode(' ', $term)) as $t) {
if (mb_strlen($t, 'UTF-8') >= 2) {
$safe = preg_replace('/' . preg_quote($t, '/') . '/iu', '<mark>$0</mark>', $safe);
}
}
return $safe;
}
/**
* Anuncio fijo (display) para el bloque bajo el buscador.
* Usa un slot de tamaño específico para mantener altura constante.
*
* @param string $size '300x250' | '728x90' | '320x100' | '970x250'
* @param string $slot Tu data-ad-slot para ese tamaño
*/
function apu_top_fixed_ad_html(string $size, string $slot): string {
$client = 'ca-pub-8476420265998726';
// whitelist de tamaños (incluye 970x250)
$allowed = ['300x250','728x90','320x100','970x250','1100x315'];
if (!in_array($size, $allowed, true)) $size = '300x250';
[$w,$h] = array_map('intval', explode('x', $size));
$w = max(1,$w); $h = max(1,$h);
$slot = preg_match('/^\d+$/', $slot) ? $slot : '0000000000'; // placeholder seguro (no muestra anuncio)
return '<div class="apu-top-ad d-flex justify-content-center">'.
'<ins class="adsbygoogle" style="display:inline-block;width:'.$w.'px;height:'.$h.'px" '.
'data-ad-client="'.$client.'" data-ad-slot="'.$slot.'"></ins>'.
'<script>(adsbygoogle = window.adsbygoogle || []).push({});</script>'.
'</div>';
}
/**
* Solo anuncios AUTO (responsive). Ignora $allowed y fuerza 'auto'.
* Puedes fijar una altura para evitar CLS: 250320 suele ir bien.
*/
function apu_random_ad_html(array $allowed = ['auto']): string {
// Alturas sugeridas: elige una fija o deja una lista si quieres variar
$heights = [100, 200, 250, 280, 300, 315, 320, 350, 400]; // puedes cambiarlo por [300] para altura constante
$h = $heights[array_rand($heights)];
return '<ins class="adsbygoogle" style="display:block; min-height:'.$h.'px"
data-ad-client="ca-pub-8476420265998726"
data-ad-slot="8471732096"
data-ad-format="auto"
data-full-width-responsive="true"></ins>
<script>(adsbygoogle = window.adsbygoogle || []).push({});</script>';
}
/**
* HTML de un anuncio AdSense aleatorio, restringido a tipos permitidos (intermedios).
*
* @param array $allowed tipos permitidos: 'auto','autorelaxed','in-article'
* @return string
*/
/*
function apu_random_ad_html(array $allowed = ['auto']): string {
$allowed = array_values(array_intersect(
array_map('strval', $allowed),
['auto','autorelaxed','in-article']
));
if (empty($allowed)) $allowed = ['auto','autorelaxed','in-article'];
$unit = $allowed[array_rand($allowed)];
if ($unit === 'auto') { // AUTO
$heights = [100, 150, 200, 250, 280, 300, 350, 400, 500, 600];
$h = $heights[array_rand($heights)];
return '<ins class="adsbygoogle" style="display:block; min-height:'.$h.'px"
data-ad-client="ca-pub-8476420265998726"
data-ad-slot="8471732096"
data-ad-format="auto"
data-full-width-responsive="true"></ins>
<script>(adsbygoogle = window.adsbygoogle || []).push({});</script>';
}
if ($unit === 'autorelaxed') { // AUTORELAXED (feed)
$heights = [280, 300, 320, 360];
$h = $heights[array_rand($heights)];
return '<ins class="adsbygoogle" style="display:block; min-height:'.$h.'px"
data-ad-format="autorelaxed"
data-ad-client="ca-pub-8476420265998726"
data-ad-slot="9205569855"></ins>
<script>(adsbygoogle = window.adsbygoogle || []).push({});</script>';
}
// IN-ARTICLE (fluid)
$heights = [180, 200, 220, 240];
$h = $heights[array_rand($heights)];
return '<ins class="adsbygoogle" style="display:block; text-align:center; min-height:'.$h.'px"
data-ad-layout="in-article"
data-ad-format="fluid"
data-ad-client="ca-pub-8476420265998726"
data-ad-slot="7285187368"></ins>
<script>(adsbygoogle = window.adsbygoogle || []).push({});</script>';
}
*/
?>
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<meta name="robots" content="noindex,nofollow">
<title>Buscador APU</title>
<!-- AdSense loader (una sola vez) -->
<script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-8476420265998726" crossorigin="anonymous"></script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css">
<style>
:root { --apu-max-width: 1150px; }
.apu-wrap { max-width: var(--apu-max-width); width: 100%; margin: 0 auto; padding-inline: 12px; }
.container { max-width: var(--apu-max-width) !important; }
body{background:#fafafa}
mark{background:#fff3cd;padding:0;border-radius:2px}
.apu-top-ad { margin-top: .5rem; margin-bottom: .75rem; } /* espacio del anuncio fijo */
</style>
</head>
<body>
<main class="apu-wrap">
<section class="container my-4">
<div class="p-3 border rounded-3 bg-light">
<form method="get" action="" class="row g-2 align-items-center">
<div class="col-12 col-md-9">
<input
type="text"
name="s"
class="form-control<?php echo (!$valid && $raw!=='') ? ' is-invalid' : ''; ?>"
placeholder="Buscar análisis… (cantidad mínima de caracteres para buscar. <?php echo (int)$cfg['limits']['min_len']; ?>)"
value="<?php echo htmlspecialchars($term ?: $raw, ENT_QUOTES, 'UTF-8'); ?>"
maxlength="<?php echo (int)$cfg['limits']['max_len']; ?>">
<?php if(!$valid && $raw!==''): ?>
<div class="invalid-feedback"><?php echo htmlspecialchars($err, ENT_QUOTES, 'UTF-8'); ?></div>
<?php endif; ?>
</div>
<div class="col-12 col-md-3 d-grid"><button class="btn btn-primary" type="submit">Buscar</button></div>
</form>
</div>
<?php
// ===== Anuncio FIJO debajo del buscador (tamaño constante) =====
echo apu_top_fixed_ad_html($TOP_FIXED_AD_SIZE, $TOP_FIXED_AD_SLOT);
?>
</section>
<?php if ($valid): ?>
<section class="container my-4">
<div class="d-flex justify-content-between align-items-center mb-4">
<h2 class="h5 mb-0">
Resultados para "<strong><?php echo htmlspecialchars($term, ENT_QUOTES, 'UTF-8'); ?></strong>":
<?php echo number_format($results['total']); ?>
</h2>
<small class="text-muted"><?php echo htmlspecialchars($results['modo']); ?> (<?php echo $results['time_ms']; ?>ms)</small>
</div>
<?php if (!empty($results['rows'])): ?>
<div class="list-group mb-4">
<?php
// ===== Anuncios entre resultados =====
$rows = $results['rows'];
$n = count($rows);
// Máximo permitido por configuración (no puede superar los huecos disponibles)
$adsBetween = min((int)$ADS_BETWEEN_MAX, max(0, $n - 1));
// Elegimos $adsBetween huecos aleatorios únicos (entre item i y i+1)
$adPos = [];
if ($adsBetween > 0) {
$pool = range(1, $n - 1); // huecos posibles
shuffle($pool);
$adPos = array_flip(array_slice($pool, 0, $adsBetween)); // set de posiciones
}
$i = 0;
foreach ($rows as $r):
$i++;
$permalink = '/?p='.(int)$r['ID'];
$clk = ($searchId)
? '/buscar-apus/clk.php?sid='.$searchId.'&pid='.(int)$r['ID'].'&pos='.$i.'&page='.$page
: $permalink; // fallback seguro
$title = (string)$r['post_title'];
$dateStr = htmlspecialchars(substr($r['post_date'],0,10), ENT_QUOTES, 'UTF-8');
?>
<a class="list-group-item list-group-item-action py-3"
href="<?php echo htmlspecialchars($clk, ENT_QUOTES, 'UTF-8'); ?>"
target="_blank" rel="noopener noreferrer">
<div class="d-flex w-100 justify-content-between mb-1">
<h3 class="h6 mb-0"><?php echo apu_highlight($title, $term); ?></h3>
</div>
</a>
<?php
// ¿Insertar anuncio después de este ítem?
if (isset($adPos[$i])) {
echo '<div class="list-group-item py-3">'.apu_random_ad_html($ALLOWED_AD_UNITS).'</div>';
}
?>
<?php endforeach; ?>
</div>
<?php
$total_pages = (int)ceil($results['total'] / $pp);
if ($total_pages > 1):
$base = $_GET; unset($base['pagina']);
?>
<nav aria-label="Paginación">
<ul class="pagination justify-content-center flex-wrap gap-1">
<?php if ($page > 1): ?>
<li class="page-item"><a class="page-link" href="?<?php echo http_build_query($base + ['pagina'=>1]); ?>">&laquo; Primera</a></li>
<li class="page-item"><a class="page-link" href="?<?php echo http_build_query($base + ['pagina'=>$page-1]); ?>">&lsaquo; Anterior</a></li>
<?php endif; ?>
<?php
$start = max(1, $page - 5);
$end = min($total_pages, $start + 9);
if ($end - $start < 9) $start = max(1, $end - 9);
for ($i = $start; $i <= $end; $i++):
?>
<li class="page-item<?php echo ($i === $page) ? ' active' : ''; ?>">
<a class="page-link" href="?<?php echo http_build_query($base + ['pagina'=>$i]); ?>"><?php echo $i; ?></a>
</li>
<?php endfor; ?>
<?php if ($page < $total_pages): ?>
<li class="page-item"><a class="page-link" href="?<?php echo http_build_query($base + ['pagina'=>$page+1]); ?>">Siguiente &rsaquo;</a></li>
<li class="page-item"><a class="page-link" href="?<?php echo http_build_query($base + ['pagina'=>$total_pages]); ?>">Última &raquo;</a></li>
<?php endif; ?>
</ul>
</nav>
<?php endif; ?>
<?php else: ?>
<div class="alert alert-secondary"><strong>Sin resultados.</strong> Prueba con términos más generales.</div>
<?php endif; ?>
</section>
<?php endif; ?>
</main>
</body>
</html>
<?php Logger::log($cfg['log'], 'fin_peticion', []); ?>