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>
This commit is contained in:
root
2025-11-03 21:04:30 -06:00
commit a22573bf0b
24068 changed files with 4993111 additions and 0 deletions

310
buscar-apus/index.php Executable file
View File

@@ -0,0 +1,310 @@
<?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', []); ?>