Generates HTML files for visual comparison before/after correction. Creates comparison_report.html for review. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
348 lines
11 KiB
PHP
348 lines
11 KiB
PHP
<?php
|
|
/**
|
|
* Validador de Correcciones - Genera archivos HTML para revisión visual
|
|
*
|
|
* PROPÓSITO: Crear archivos comparativos ANTES/DESPUÉS para validar
|
|
* que la corrección no rompe el contenido.
|
|
*
|
|
* USO: php validate-fix-lists.php
|
|
*
|
|
* GENERA:
|
|
* /tmp/list-fix-validation/
|
|
* ├── post_ID_before.html
|
|
* ├── post_ID_after.html
|
|
* └── comparison_report.html
|
|
*
|
|
* @package ROI_Theme
|
|
*/
|
|
|
|
error_reporting(E_ALL);
|
|
ini_set('display_errors', 1);
|
|
ini_set('memory_limit', '256M');
|
|
|
|
$db_config = [
|
|
'host' => 'localhost',
|
|
'database' => 'preciosunitarios_seo',
|
|
'username' => 'preciosunitarios_seo',
|
|
'password' => 'ACl%EEFd=V-Yvb??',
|
|
'charset' => 'utf8mb4'
|
|
];
|
|
|
|
$output_dir = '/tmp/list-fix-validation';
|
|
$sample_size = 5;
|
|
|
|
echo "==============================================\n";
|
|
echo " VALIDADOR DE CORRECCIONES\n";
|
|
echo " Fecha: " . date('Y-m-d H:i:s') . "\n";
|
|
echo "==============================================\n\n";
|
|
|
|
// Crear directorio de salida
|
|
if (!is_dir($output_dir)) {
|
|
mkdir($output_dir, 0755, true);
|
|
}
|
|
|
|
// Limpiar archivos anteriores
|
|
array_map('unlink', glob("$output_dir/*.html"));
|
|
|
|
/**
|
|
* Detectar problemas en HTML
|
|
*/
|
|
function detectIssues(string $html): array {
|
|
$issues = [];
|
|
libxml_use_internal_errors(true);
|
|
|
|
$doc = new DOMDocument('1.0', 'UTF-8');
|
|
$wrapped = '<div id="temp-wrapper">' . $html . '</div>';
|
|
$doc->loadHTML('<?xml encoding="UTF-8">' . $wrapped, LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD);
|
|
libxml_clear_errors();
|
|
|
|
$validChildren = ['li', 'script', 'template'];
|
|
|
|
foreach (['ul', 'ol'] as $listTag) {
|
|
foreach ($doc->getElementsByTagName($listTag) as $list) {
|
|
foreach ($list->childNodes as $child) {
|
|
if ($child->nodeType === XML_ELEMENT_NODE) {
|
|
$tagName = strtolower($child->nodeName);
|
|
if (!in_array($tagName, $validChildren)) {
|
|
$issues[] = "<$listTag> contiene <$tagName>";
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return $issues;
|
|
}
|
|
|
|
/**
|
|
* Corregir listas mal formadas
|
|
*/
|
|
function fixMalformedLists(string $html): array {
|
|
$result = ['fixed' => false, 'html' => $html, 'changes' => 0, 'details' => []];
|
|
|
|
libxml_use_internal_errors(true);
|
|
$doc = new DOMDocument('1.0', 'UTF-8');
|
|
$wrapped = '<div id="temp-wrapper">' . $html . '</div>';
|
|
$doc->loadHTML('<?xml encoding="UTF-8">' . $wrapped, LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD);
|
|
libxml_clear_errors();
|
|
|
|
$lists = [];
|
|
foreach ($doc->getElementsByTagName('ul') as $ul) { $lists[] = $ul; }
|
|
foreach ($doc->getElementsByTagName('ol') as $ol) { $lists[] = $ol; }
|
|
|
|
$changes = 0;
|
|
$validChildren = ['li', 'script', 'template'];
|
|
|
|
foreach ($lists as $list) {
|
|
$nodesToProcess = [];
|
|
foreach ($list->childNodes as $child) {
|
|
if ($child->nodeType === XML_ELEMENT_NODE) {
|
|
$tagName = strtolower($child->nodeName);
|
|
if (!in_array($tagName, $validChildren)) {
|
|
$nodesToProcess[] = $child;
|
|
}
|
|
}
|
|
}
|
|
|
|
foreach ($nodesToProcess as $node) {
|
|
$tagName = strtolower($node->nodeName);
|
|
$prevLi = null;
|
|
$prev = $node->previousSibling;
|
|
|
|
while ($prev) {
|
|
if ($prev->nodeType === XML_ELEMENT_NODE && strtolower($prev->nodeName) === 'li') {
|
|
$prevLi = $prev;
|
|
break;
|
|
}
|
|
$prev = $prev->previousSibling;
|
|
}
|
|
|
|
if ($prevLi) {
|
|
$prevLi->appendChild($node);
|
|
$result['details'][] = "Movido <$tagName> dentro del <li> anterior";
|
|
$changes++;
|
|
} else {
|
|
$newLi = $doc->createElement('li');
|
|
$list->insertBefore($newLi, $node);
|
|
$newLi->appendChild($node);
|
|
$result['details'][] = "Envuelto <$tagName> en nuevo <li>";
|
|
$changes++;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ($changes > 0) {
|
|
$wrapper = $doc->getElementById('temp-wrapper');
|
|
if ($wrapper) {
|
|
$innerHTML = '';
|
|
foreach ($wrapper->childNodes as $child) {
|
|
$innerHTML .= $doc->saveHTML($child);
|
|
}
|
|
$result['html'] = $innerHTML;
|
|
$result['fixed'] = true;
|
|
$result['changes'] = $changes;
|
|
}
|
|
}
|
|
|
|
return $result;
|
|
}
|
|
|
|
/**
|
|
* Generar HTML wrapper para visualización
|
|
*/
|
|
function wrapForVisualization(string $content, string $title, string $status): string {
|
|
$statusColor = $status === 'error' ? '#dc3545' : '#28a745';
|
|
return <<<HTML
|
|
<!DOCTYPE html>
|
|
<html lang="es">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>$title</title>
|
|
<style>
|
|
body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; margin: 20px; line-height: 1.6; }
|
|
.status { padding: 10px 20px; background: $statusColor; color: white; border-radius: 4px; margin-bottom: 20px; }
|
|
.content { border: 1px solid #ddd; padding: 20px; border-radius: 4px; background: #fafafa; }
|
|
ul, ol { background: #fff3cd; padding: 15px 15px 15px 35px; border-left: 4px solid #ffc107; margin: 10px 0; }
|
|
li { background: #d4edda; padding: 5px 10px; margin: 5px 0; border-left: 3px solid #28a745; }
|
|
h1, h2, h3, h4, h5, h6 { color: #333; }
|
|
p { color: #555; }
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div class="status">$status</div>
|
|
<div class="content">
|
|
$content
|
|
</div>
|
|
</body>
|
|
</html>
|
|
HTML;
|
|
}
|
|
|
|
// Conectar a DB
|
|
$conn = new mysqli($db_config['host'], $db_config['username'], $db_config['password'], $db_config['database']);
|
|
$conn->set_charset($db_config['charset']);
|
|
|
|
if ($conn->connect_error) {
|
|
die("Error de conexión: " . $conn->connect_error);
|
|
}
|
|
|
|
echo "✓ Conexión establecida\n\n";
|
|
|
|
// Buscar posts con problemas
|
|
$query = "SELECT id, page, html FROM datos_seo_pagina WHERE html IS NOT NULL AND html != '' ORDER BY id LIMIT 500";
|
|
$result = $conn->query($query);
|
|
|
|
$samples = [];
|
|
while ($row = $result->fetch_assoc()) {
|
|
$issues = detectIssues($row['html']);
|
|
if (!empty($issues) && count($samples) < $sample_size) {
|
|
$samples[] = $row;
|
|
}
|
|
}
|
|
|
|
echo "Encontrados " . count($samples) . " posts con problemas para validar\n\n";
|
|
|
|
$comparison_data = [];
|
|
|
|
foreach ($samples as $idx => $post) {
|
|
$id = $post['id'];
|
|
$url = $post['page'];
|
|
$html_before = $post['html'];
|
|
|
|
echo "─────────────────────────────────\n";
|
|
echo "POST $id: $url\n";
|
|
|
|
// Detectar problemas antes
|
|
$issues_before = detectIssues($html_before);
|
|
echo " Problemas ANTES: " . count($issues_before) . "\n";
|
|
|
|
// Aplicar corrección
|
|
$fixResult = fixMalformedLists($html_before);
|
|
$html_after = $fixResult['html'];
|
|
|
|
// Detectar problemas después
|
|
$issues_after = detectIssues($html_after);
|
|
echo " Problemas DESPUÉS: " . count($issues_after) . "\n";
|
|
echo " Cambios aplicados: " . $fixResult['changes'] . "\n";
|
|
|
|
// Guardar archivos HTML
|
|
$file_before = "$output_dir/post_{$id}_BEFORE.html";
|
|
$file_after = "$output_dir/post_{$id}_AFTER.html";
|
|
|
|
file_put_contents($file_before, wrapForVisualization(
|
|
$html_before,
|
|
"Post $id - ANTES (con errores)",
|
|
"ANTES: " . count($issues_before) . " problemas de listas"
|
|
));
|
|
|
|
file_put_contents($file_after, wrapForVisualization(
|
|
$html_after,
|
|
"Post $id - DESPUÉS (corregido)",
|
|
"DESPUÉS: " . count($issues_after) . " problemas - " . $fixResult['changes'] . " correcciones aplicadas"
|
|
));
|
|
|
|
echo " ✓ Archivos generados:\n";
|
|
echo " - $file_before\n";
|
|
echo " - $file_after\n";
|
|
|
|
// Guardar datos para reporte
|
|
$comparison_data[] = [
|
|
'id' => $id,
|
|
'url' => $url,
|
|
'issues_before' => count($issues_before),
|
|
'issues_after' => count($issues_after),
|
|
'changes' => $fixResult['changes'],
|
|
'file_before' => "post_{$id}_BEFORE.html",
|
|
'file_after' => "post_{$id}_AFTER.html"
|
|
];
|
|
}
|
|
|
|
// Generar reporte comparativo
|
|
$report_html = <<<HTML
|
|
<!DOCTYPE html>
|
|
<html lang="es">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<title>Reporte de Validación - Corrección de Listas</title>
|
|
<style>
|
|
body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; margin: 20px; }
|
|
h1 { color: #333; border-bottom: 2px solid #007bff; padding-bottom: 10px; }
|
|
table { width: 100%; border-collapse: collapse; margin: 20px 0; }
|
|
th, td { padding: 12px; text-align: left; border: 1px solid #ddd; }
|
|
th { background: #007bff; color: white; }
|
|
tr:nth-child(even) { background: #f8f9fa; }
|
|
.success { color: #28a745; font-weight: bold; }
|
|
.warning { color: #ffc107; font-weight: bold; }
|
|
.error { color: #dc3545; font-weight: bold; }
|
|
a { color: #007bff; text-decoration: none; }
|
|
a:hover { text-decoration: underline; }
|
|
.instructions { background: #e7f3ff; padding: 15px; border-radius: 4px; margin: 20px 0; }
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<h1>Reporte de Validación - Corrección de Listas HTML</h1>
|
|
|
|
<div class="instructions">
|
|
<strong>Instrucciones:</strong>
|
|
<ol>
|
|
<li>Abre cada par de archivos (ANTES/DESPUÉS) en el navegador</li>
|
|
<li>Verifica que el contenido se muestre correctamente</li>
|
|
<li>Las listas (fondo amarillo) deben contener solo items (fondo verde)</li>
|
|
<li>Si todo se ve bien, la corrección es segura</li>
|
|
</ol>
|
|
</div>
|
|
|
|
<table>
|
|
<thead>
|
|
<tr>
|
|
<th>ID</th>
|
|
<th>URL</th>
|
|
<th>Problemas Antes</th>
|
|
<th>Problemas Después</th>
|
|
<th>Cambios</th>
|
|
<th>Archivos</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
HTML;
|
|
|
|
foreach ($comparison_data as $data) {
|
|
$status_class = $data['issues_after'] == 0 ? 'success' : ($data['issues_after'] < $data['issues_before'] ? 'warning' : 'error');
|
|
|
|
$report_html .= <<<HTML
|
|
<tr>
|
|
<td>{$data['id']}</td>
|
|
<td><a href="{$data['url']}" target="_blank">{$data['url']}</a></td>
|
|
<td class="error">{$data['issues_before']}</td>
|
|
<td class="$status_class">{$data['issues_after']}</td>
|
|
<td>{$data['changes']}</td>
|
|
<td>
|
|
<a href="{$data['file_before']}" target="_blank">ANTES</a> |
|
|
<a href="{$data['file_after']}" target="_blank">DESPUÉS</a>
|
|
</td>
|
|
</tr>
|
|
HTML;
|
|
}
|
|
|
|
$report_html .= <<<HTML
|
|
</tbody>
|
|
</table>
|
|
|
|
<p><strong>Generado:</strong> {$_SERVER['REQUEST_TIME_FLOAT']}</p>
|
|
</body>
|
|
</html>
|
|
HTML;
|
|
|
|
$report_file = "$output_dir/comparison_report.html";
|
|
file_put_contents($report_file, $report_html);
|
|
|
|
echo "\n─────────────────────────────────\n";
|
|
echo "REPORTE GENERADO:\n";
|
|
echo " $report_file\n\n";
|
|
echo "Para revisar, descarga el directorio:\n";
|
|
echo " scp -r VPSContabo:$output_dir ./validation/\n\n";
|
|
|
|
$conn->close();
|
|
echo "✓ Validación completada.\n";
|