chore: purgar archivos no utilizados (plan 101 fase 1)
- eliminar carpetas vacias admin/herosection y bootstrapicons - eliminar 7 scripts legacy con credenciales hardcodeadas - eliminar formbuilder duplicado en shared/infrastructure/ui - eliminar 11 archivos .gitkeep en carpetas con contenido 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -1 +0,0 @@
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
|
||||
@@ -1,94 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Busca casos variados de problemas de listas para validación exhaustiva
|
||||
*/
|
||||
|
||||
$conn = new mysqli("localhost", "preciosunitarios_seo", "ACl%EEFd=V-Yvb??", "preciosunitarios_seo");
|
||||
$conn->set_charset("utf8mb4");
|
||||
|
||||
function detectIssues($html) {
|
||||
$issues = [];
|
||||
libxml_use_internal_errors(true);
|
||||
$doc = new DOMDocument("1.0", "UTF-8");
|
||||
$wrapped = '<div id="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 $tag) {
|
||||
foreach ($doc->getElementsByTagName($tag) as $list) {
|
||||
foreach ($list->childNodes as $child) {
|
||||
if ($child->nodeType === XML_ELEMENT_NODE) {
|
||||
$childTag = strtolower($child->nodeName);
|
||||
if (!in_array($childTag, $validChildren)) {
|
||||
$issues[] = ["parent" => $tag, "child" => $childTag];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return $issues;
|
||||
}
|
||||
|
||||
echo "BUSCANDO CASOS VARIADOS...\n\n";
|
||||
|
||||
$query = "SELECT id, page, html FROM datos_seo_pagina WHERE html IS NOT NULL AND html != '' ORDER BY id";
|
||||
$result = $conn->query($query);
|
||||
|
||||
if (!$result) {
|
||||
die("Error en query: " . $conn->error);
|
||||
}
|
||||
|
||||
$cases = [
|
||||
"many_issues" => [],
|
||||
"ol_issues" => [],
|
||||
"mixed_issues" => [],
|
||||
"few_issues" => []
|
||||
];
|
||||
|
||||
while ($row = $result->fetch_assoc()) {
|
||||
$issues = detectIssues($row["html"]);
|
||||
if (empty($issues)) continue;
|
||||
|
||||
$count = count($issues);
|
||||
$hasOl = false;
|
||||
$hasUl = false;
|
||||
|
||||
foreach ($issues as $issue) {
|
||||
if ($issue["parent"] === "ol") $hasOl = true;
|
||||
if ($issue["parent"] === "ul") $hasUl = true;
|
||||
}
|
||||
|
||||
if ($count > 10 && count($cases["many_issues"]) < 3) {
|
||||
$cases["many_issues"][] = ["id" => $row["id"], "url" => $row["page"], "count" => $count, "issues" => $issues];
|
||||
}
|
||||
if ($hasOl && !$hasUl && count($cases["ol_issues"]) < 3) {
|
||||
$cases["ol_issues"][] = ["id" => $row["id"], "url" => $row["page"], "count" => $count, "issues" => $issues];
|
||||
}
|
||||
if ($hasOl && $hasUl && count($cases["mixed_issues"]) < 3) {
|
||||
$cases["mixed_issues"][] = ["id" => $row["id"], "url" => $row["page"], "count" => $count, "issues" => $issues];
|
||||
}
|
||||
if ($count <= 2 && count($cases["few_issues"]) < 3) {
|
||||
$cases["few_issues"][] = ["id" => $row["id"], "url" => $row["page"], "count" => $count, "issues" => $issues];
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($cases as $type => $posts) {
|
||||
echo "=== " . strtoupper($type) . " ===\n";
|
||||
if (empty($posts)) {
|
||||
echo " (ninguno encontrado)\n\n";
|
||||
continue;
|
||||
}
|
||||
foreach ($posts as $post) {
|
||||
echo "ID: {$post["id"]} - {$post["count"]} problemas\n";
|
||||
echo "URL: {$post["url"]}\n";
|
||||
echo "Tipos: ";
|
||||
$types = [];
|
||||
foreach ($post["issues"] as $i) {
|
||||
$types[] = "<{$i["parent"]}> contiene <{$i["child"]}>";
|
||||
}
|
||||
echo implode(", ", array_unique($types)) . "\n\n";
|
||||
}
|
||||
}
|
||||
|
||||
$conn->close();
|
||||
@@ -1,411 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Corrector de Listas HTML Mal Formadas usando DOMDocument
|
||||
*
|
||||
* PROPÓSITO: Detectar y corregir listas con estructura inválida
|
||||
* - <ul>/<ol> conteniendo elementos no-<li> como hijos directos
|
||||
* - Listas anidadas que son hermanas en lugar de hijas de <li>
|
||||
*
|
||||
* USO:
|
||||
* php fix-malformed-lists-dom.php --mode=scan # Solo escanear
|
||||
* php fix-malformed-lists-dom.php --mode=test # Probar corrección (1 post)
|
||||
* php fix-malformed-lists-dom.php --mode=fix # Aplicar correcciones
|
||||
*
|
||||
* @package ROI_Theme
|
||||
* @since Phase 4.4 Accessibility
|
||||
*/
|
||||
|
||||
error_reporting(E_ALL);
|
||||
ini_set('display_errors', 1);
|
||||
ini_set('memory_limit', '512M');
|
||||
set_time_limit(600);
|
||||
|
||||
// Configuración
|
||||
$db_config = [
|
||||
'host' => 'localhost',
|
||||
'database' => 'preciosunitarios_seo',
|
||||
'username' => 'preciosunitarios_seo',
|
||||
'password' => 'ACl%EEFd=V-Yvb??',
|
||||
'charset' => 'utf8mb4'
|
||||
];
|
||||
|
||||
// Parsear argumentos
|
||||
$mode = 'scan';
|
||||
foreach ($argv as $arg) {
|
||||
if (strpos($arg, '--mode=') === 0) {
|
||||
$mode = substr($arg, 7);
|
||||
}
|
||||
}
|
||||
|
||||
echo "==============================================\n";
|
||||
echo " CORRECTOR DE LISTAS - DOMDocument\n";
|
||||
echo " Modo: $mode\n";
|
||||
echo " Fecha: " . date('Y-m-d H:i:s') . "\n";
|
||||
echo "==============================================\n\n";
|
||||
|
||||
/**
|
||||
* Conectar a la base de datos
|
||||
*/
|
||||
function connectDatabase(array $config): ?mysqli {
|
||||
$conn = new mysqli(
|
||||
$config['host'],
|
||||
$config['username'],
|
||||
$config['password'],
|
||||
$config['database']
|
||||
);
|
||||
if ($conn->connect_error) {
|
||||
echo "Error de conexión: " . $conn->connect_error . "\n";
|
||||
return null;
|
||||
}
|
||||
$conn->set_charset($config['charset']);
|
||||
return $conn;
|
||||
}
|
||||
|
||||
/**
|
||||
* Corregir listas mal formadas usando DOMDocument
|
||||
*/
|
||||
function fixMalformedLists(string $html): array {
|
||||
$result = [
|
||||
'fixed' => false,
|
||||
'html' => $html,
|
||||
'changes' => 0,
|
||||
'details' => []
|
||||
];
|
||||
|
||||
// Suprimir errores de HTML mal formado
|
||||
libxml_use_internal_errors(true);
|
||||
|
||||
$doc = new DOMDocument('1.0', 'UTF-8');
|
||||
|
||||
// Envolver en contenedor para preservar estructura
|
||||
$wrapped = '<div id="temp-wrapper">' . $html . '</div>';
|
||||
$doc->loadHTML('<?xml encoding="UTF-8">' . $wrapped, LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD);
|
||||
|
||||
libxml_clear_errors();
|
||||
|
||||
// Procesar todas las listas (ul y ol)
|
||||
$lists = [];
|
||||
foreach ($doc->getElementsByTagName('ul') as $ul) {
|
||||
$lists[] = $ul;
|
||||
}
|
||||
foreach ($doc->getElementsByTagName('ol') as $ol) {
|
||||
$lists[] = $ol;
|
||||
}
|
||||
|
||||
$changes = 0;
|
||||
|
||||
foreach ($lists as $list) {
|
||||
$changes += fixListChildren($list, $result['details']);
|
||||
}
|
||||
|
||||
if ($changes > 0) {
|
||||
// Extraer HTML corregido
|
||||
$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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Corregir hijos de una lista (solo debe contener li, script, template)
|
||||
*/
|
||||
function fixListChildren(DOMElement $list, array &$details): int {
|
||||
$changes = 0;
|
||||
$validChildren = ['li', 'script', 'template'];
|
||||
$nodesToProcess = [];
|
||||
|
||||
// Recopilar nodos que necesitan corrección
|
||||
foreach ($list->childNodes as $child) {
|
||||
if ($child->nodeType === XML_ELEMENT_NODE) {
|
||||
$tagName = strtolower($child->nodeName);
|
||||
if (!in_array($tagName, $validChildren)) {
|
||||
$nodesToProcess[] = $child;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Procesar cada nodo inválido
|
||||
foreach ($nodesToProcess as $node) {
|
||||
$tagName = strtolower($node->nodeName);
|
||||
|
||||
// Si es una lista anidada (ul/ol), envolverla en <li>
|
||||
if ($tagName === 'ul' || $tagName === 'ol') {
|
||||
$changes += wrapInLi($list, $node, $details);
|
||||
}
|
||||
// Otros elementos inválidos también se envuelven en <li>
|
||||
else {
|
||||
$changes += wrapInLi($list, $node, $details);
|
||||
}
|
||||
}
|
||||
|
||||
return $changes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Envolver un nodo en <li> o moverlo al <li> anterior
|
||||
*/
|
||||
function wrapInLi(DOMElement $list, DOMNode $node, array &$details): int {
|
||||
$doc = $list->ownerDocument;
|
||||
$tagName = strtolower($node->nodeName);
|
||||
|
||||
// Buscar el <li> hermano anterior
|
||||
$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) {
|
||||
// Mover el nodo al final del <li> anterior
|
||||
$prevLi->appendChild($node);
|
||||
$details[] = "Movido <$tagName> dentro del <li> anterior";
|
||||
return 1;
|
||||
} else {
|
||||
// No hay <li> anterior, crear uno nuevo
|
||||
$newLi = $doc->createElement('li');
|
||||
$list->insertBefore($newLi, $node);
|
||||
$newLi->appendChild($node);
|
||||
$details[] = "Envuelto <$tagName> en nuevo <li>";
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Detectar problemas en HTML sin corregir
|
||||
*/
|
||||
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'];
|
||||
|
||||
// Revisar ul
|
||||
foreach ($doc->getElementsByTagName('ul') as $ul) {
|
||||
foreach ($ul->childNodes as $child) {
|
||||
if ($child->nodeType === XML_ELEMENT_NODE) {
|
||||
$tagName = strtolower($child->nodeName);
|
||||
if (!in_array($tagName, $validChildren)) {
|
||||
$issues[] = [
|
||||
'list_type' => 'ul',
|
||||
'invalid_child' => $tagName,
|
||||
'context' => getNodeContext($child)
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Revisar ol
|
||||
foreach ($doc->getElementsByTagName('ol') as $ol) {
|
||||
foreach ($ol->childNodes as $child) {
|
||||
if ($child->nodeType === XML_ELEMENT_NODE) {
|
||||
$tagName = strtolower($child->nodeName);
|
||||
if (!in_array($tagName, $validChildren)) {
|
||||
$issues[] = [
|
||||
'list_type' => 'ol',
|
||||
'invalid_child' => $tagName,
|
||||
'context' => getNodeContext($child)
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $issues;
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtener contexto de un nodo para debug
|
||||
*/
|
||||
function getNodeContext(DOMNode $node): string {
|
||||
$doc = $node->ownerDocument;
|
||||
$html = $doc->saveHTML($node);
|
||||
return substr($html, 0, 100) . (strlen($html) > 100 ? '...' : '');
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// EJECUCIÓN PRINCIPAL
|
||||
// ============================================
|
||||
|
||||
$conn = connectDatabase($db_config);
|
||||
if (!$conn) {
|
||||
exit(1);
|
||||
}
|
||||
|
||||
echo "✓ Conexión establecida\n\n";
|
||||
|
||||
// Contar registros
|
||||
$result = $conn->query("SELECT COUNT(*) as total FROM datos_seo_pagina WHERE html IS NOT NULL AND html != ''");
|
||||
$total = $result->fetch_assoc()['total'];
|
||||
echo "Total de registros: $total\n\n";
|
||||
|
||||
if ($mode === 'scan') {
|
||||
// MODO SCAN: Solo detectar problemas
|
||||
echo "MODO: ESCANEO (solo detección)\n";
|
||||
echo "─────────────────────────────────\n\n";
|
||||
|
||||
$batch_size = 100;
|
||||
$offset = 0;
|
||||
$affected = 0;
|
||||
$total_issues = 0;
|
||||
|
||||
while ($offset < $total) {
|
||||
$query = "SELECT id, page, html FROM datos_seo_pagina
|
||||
WHERE html IS NOT NULL AND html != ''
|
||||
ORDER BY id LIMIT $batch_size OFFSET $offset";
|
||||
$result = $conn->query($query);
|
||||
|
||||
while ($row = $result->fetch_assoc()) {
|
||||
$issues = detectIssues($row['html']);
|
||||
if (!empty($issues)) {
|
||||
$affected++;
|
||||
$total_issues += count($issues);
|
||||
|
||||
if ($affected <= 20) {
|
||||
echo "[ID: {$row['id']}] " . count($issues) . " problema(s)\n";
|
||||
echo "URL: {$row['page']}\n";
|
||||
foreach (array_slice($issues, 0, 2) as $issue) {
|
||||
echo " - <{$issue['list_type']}> contiene <{$issue['invalid_child']}>\n";
|
||||
}
|
||||
echo "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
$offset += $batch_size;
|
||||
|
||||
if ($offset % 1000 == 0) {
|
||||
echo "Procesados: $offset/$total...\n";
|
||||
}
|
||||
}
|
||||
|
||||
echo "─────────────────────────────────\n";
|
||||
echo "RESUMEN:\n";
|
||||
echo " Posts afectados: $affected\n";
|
||||
echo " Total incidencias: $total_issues\n";
|
||||
|
||||
} elseif ($mode === 'test') {
|
||||
// MODO TEST: Probar corrección en 1 post
|
||||
echo "MODO: PRUEBA (sin guardar)\n";
|
||||
echo "─────────────────────────────────\n\n";
|
||||
|
||||
// Buscar primer post con problemas
|
||||
$query = "SELECT id, page, html FROM datos_seo_pagina
|
||||
WHERE html IS NOT NULL AND html != ''
|
||||
ORDER BY id LIMIT 100";
|
||||
$result = $conn->query($query);
|
||||
|
||||
while ($row = $result->fetch_assoc()) {
|
||||
$issues = detectIssues($row['html']);
|
||||
if (!empty($issues)) {
|
||||
echo "POST ID: {$row['id']}\n";
|
||||
echo "URL: {$row['page']}\n";
|
||||
echo "Problemas detectados: " . count($issues) . "\n\n";
|
||||
|
||||
echo "ANTES (problemas):\n";
|
||||
foreach (array_slice($issues, 0, 3) as $issue) {
|
||||
echo " - <{$issue['list_type']}> contiene <{$issue['invalid_child']}>\n";
|
||||
echo " Contexto: " . htmlspecialchars(substr($issue['context'], 0, 80)) . "\n";
|
||||
}
|
||||
|
||||
// Aplicar corrección
|
||||
$fixResult = fixMalformedLists($row['html']);
|
||||
|
||||
echo "\nDESPUÉS (corrección):\n";
|
||||
echo " Cambios realizados: {$fixResult['changes']}\n";
|
||||
foreach ($fixResult['details'] as $detail) {
|
||||
echo " - $detail\n";
|
||||
}
|
||||
|
||||
// Verificar que no quedan problemas
|
||||
$issuesAfter = detectIssues($fixResult['html']);
|
||||
echo "\nVERIFICACIÓN:\n";
|
||||
echo " Problemas antes: " . count($issues) . "\n";
|
||||
echo " Problemas después: " . count($issuesAfter) . "\n";
|
||||
|
||||
if (count($issuesAfter) < count($issues)) {
|
||||
echo " ✓ Reducción de problemas\n";
|
||||
}
|
||||
|
||||
// Mostrar fragmento del HTML corregido
|
||||
if ($fixResult['fixed']) {
|
||||
echo "\nMUESTRA HTML CORREGIDO (primeros 500 chars):\n";
|
||||
echo "─────────────────────────────────\n";
|
||||
echo htmlspecialchars(substr($fixResult['html'], 0, 500)) . "...\n";
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
} elseif ($mode === 'fix') {
|
||||
// MODO FIX: Aplicar correcciones
|
||||
echo "MODO: CORRECCIÓN (GUARDANDO CAMBIOS)\n";
|
||||
echo "─────────────────────────────────\n\n";
|
||||
|
||||
$batch_size = 50;
|
||||
$offset = 0;
|
||||
$fixed_count = 0;
|
||||
$error_count = 0;
|
||||
|
||||
while ($offset < $total) {
|
||||
$query = "SELECT id, page, html FROM datos_seo_pagina
|
||||
WHERE html IS NOT NULL AND html != ''
|
||||
ORDER BY id LIMIT $batch_size OFFSET $offset";
|
||||
$result = $conn->query($query);
|
||||
|
||||
while ($row = $result->fetch_assoc()) {
|
||||
$issues = detectIssues($row['html']);
|
||||
|
||||
if (!empty($issues)) {
|
||||
$fixResult = fixMalformedLists($row['html']);
|
||||
|
||||
if ($fixResult['fixed']) {
|
||||
// Guardar HTML corregido
|
||||
$stmt = $conn->prepare("UPDATE datos_seo_pagina SET html = ? WHERE id = ?");
|
||||
$stmt->bind_param("si", $fixResult['html'], $row['id']);
|
||||
|
||||
if ($stmt->execute()) {
|
||||
$fixed_count++;
|
||||
echo "[ID: {$row['id']}] ✓ Corregido ({$fixResult['changes']} cambios)\n";
|
||||
} else {
|
||||
$error_count++;
|
||||
echo "[ID: {$row['id']}] ✗ Error al guardar\n";
|
||||
}
|
||||
$stmt->close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$offset += $batch_size;
|
||||
|
||||
if ($offset % 500 == 0) {
|
||||
echo "Procesados: $offset/$total (corregidos: $fixed_count)\n";
|
||||
}
|
||||
}
|
||||
|
||||
echo "\n─────────────────────────────────\n";
|
||||
echo "RESUMEN:\n";
|
||||
echo " Posts corregidos: $fixed_count\n";
|
||||
echo " Errores: $error_count\n";
|
||||
}
|
||||
|
||||
$conn->close();
|
||||
echo "\n✓ Proceso completado.\n";
|
||||
@@ -1,322 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Corrector de Listas HTML Mal Formadas - WordPress Posts
|
||||
*
|
||||
* BASE DE DATOS: preciosunitarios_wp
|
||||
* TABLA: wp_posts
|
||||
* CAMPO: post_content
|
||||
*
|
||||
* USO:
|
||||
* php fix-malformed-lists-wp-posts.php --mode=scan
|
||||
* php fix-malformed-lists-wp-posts.php --mode=test
|
||||
* php fix-malformed-lists-wp-posts.php --mode=fix
|
||||
*
|
||||
* @package ROI_Theme
|
||||
*/
|
||||
|
||||
error_reporting(E_ALL);
|
||||
ini_set('display_errors', 1);
|
||||
ini_set('memory_limit', '512M');
|
||||
set_time_limit(600);
|
||||
|
||||
$db_config = [
|
||||
'host' => 'localhost',
|
||||
'database' => 'preciosunitarios_wp',
|
||||
'username' => 'preciosunitarios_wp',
|
||||
'password' => 'Kq#Gk%yEt+PWpVe&HZ',
|
||||
'charset' => 'utf8mb4'
|
||||
];
|
||||
|
||||
$mode = 'scan';
|
||||
foreach ($argv as $arg) {
|
||||
if (strpos($arg, '--mode=') === 0) {
|
||||
$mode = substr($arg, 7);
|
||||
}
|
||||
}
|
||||
|
||||
echo "==============================================\n";
|
||||
echo " CORRECTOR DE LISTAS - WordPress Posts\n";
|
||||
echo " Base de datos: {$db_config['database']}\n";
|
||||
echo " Tabla: wp_posts (post_content)\n";
|
||||
echo " Modo: $mode\n";
|
||||
echo " Fecha: " . date('Y-m-d H:i:s') . "\n";
|
||||
echo "==============================================\n\n";
|
||||
|
||||
function connectDatabase(array $config): ?mysqli {
|
||||
$conn = new mysqli($config['host'], $config['username'], $config['password'], $config['database']);
|
||||
if ($conn->connect_error) {
|
||||
echo "Error de conexión: " . $conn->connect_error . "\n";
|
||||
return null;
|
||||
}
|
||||
$conn->set_charset($config['charset']);
|
||||
return $conn;
|
||||
}
|
||||
|
||||
function detectIssues(string $html): array {
|
||||
$issues = [];
|
||||
if (empty(trim($html))) return $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[] = [
|
||||
'list_type' => $listTag,
|
||||
'invalid_child' => $tagName
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $issues;
|
||||
}
|
||||
|
||||
function fixMalformedLists(string $html): array {
|
||||
$result = ['fixed' => false, 'html' => $html, 'changes' => 0, 'details' => []];
|
||||
|
||||
if (empty(trim($html))) return $result;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
// EJECUCIÓN PRINCIPAL
|
||||
$conn = connectDatabase($db_config);
|
||||
if (!$conn) {
|
||||
exit(1);
|
||||
}
|
||||
|
||||
echo "✓ Conexión establecida\n\n";
|
||||
|
||||
// Solo posts publicados con contenido
|
||||
$countQuery = "SELECT COUNT(*) as total FROM wp_posts
|
||||
WHERE post_status = 'publish'
|
||||
AND post_type IN ('post', 'page')
|
||||
AND post_content IS NOT NULL
|
||||
AND post_content != ''";
|
||||
$result = $conn->query($countQuery);
|
||||
$total = $result->fetch_assoc()['total'];
|
||||
echo "Total de posts/páginas publicados: $total\n\n";
|
||||
|
||||
if ($mode === 'scan') {
|
||||
echo "MODO: ESCANEO (solo detección)\n";
|
||||
echo "─────────────────────────────────\n\n";
|
||||
|
||||
$batch_size = 100;
|
||||
$offset = 0;
|
||||
$affected = 0;
|
||||
$total_issues = 0;
|
||||
|
||||
while ($offset < $total) {
|
||||
$query = "SELECT ID, post_title, post_content, guid FROM wp_posts
|
||||
WHERE post_status = 'publish'
|
||||
AND post_type IN ('post', 'page')
|
||||
AND post_content IS NOT NULL
|
||||
AND post_content != ''
|
||||
ORDER BY ID LIMIT $batch_size OFFSET $offset";
|
||||
$result = $conn->query($query);
|
||||
|
||||
while ($row = $result->fetch_assoc()) {
|
||||
$issues = detectIssues($row['post_content']);
|
||||
if (!empty($issues)) {
|
||||
$affected++;
|
||||
$total_issues += count($issues);
|
||||
|
||||
if ($affected <= 20) {
|
||||
echo "[ID: {$row['ID']}] " . count($issues) . " problema(s)\n";
|
||||
echo "Título: " . substr($row['post_title'], 0, 60) . "\n";
|
||||
foreach (array_slice($issues, 0, 2) as $issue) {
|
||||
echo " - <{$issue['list_type']}> contiene <{$issue['invalid_child']}>\n";
|
||||
}
|
||||
echo "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
$offset += $batch_size;
|
||||
|
||||
if ($offset % 1000 == 0) {
|
||||
echo "Procesados: $offset/$total...\n";
|
||||
}
|
||||
}
|
||||
|
||||
echo "─────────────────────────────────\n";
|
||||
echo "RESUMEN:\n";
|
||||
echo " Posts afectados: $affected\n";
|
||||
echo " Total incidencias: $total_issues\n";
|
||||
|
||||
} elseif ($mode === 'test') {
|
||||
echo "MODO: PRUEBA (sin guardar)\n";
|
||||
echo "─────────────────────────────────\n\n";
|
||||
|
||||
$query = "SELECT ID, post_title, post_content FROM wp_posts
|
||||
WHERE post_status = 'publish'
|
||||
AND post_type IN ('post', 'page')
|
||||
AND post_content IS NOT NULL
|
||||
AND post_content != ''
|
||||
ORDER BY ID LIMIT 200";
|
||||
$result = $conn->query($query);
|
||||
|
||||
$tested = 0;
|
||||
while ($row = $result->fetch_assoc()) {
|
||||
$issues = detectIssues($row['post_content']);
|
||||
if (!empty($issues) && $tested < 5) {
|
||||
$tested++;
|
||||
echo "POST ID: {$row['ID']}\n";
|
||||
echo "Título: {$row['post_title']}\n";
|
||||
echo "Problemas detectados: " . count($issues) . "\n\n";
|
||||
|
||||
$fixResult = fixMalformedLists($row['post_content']);
|
||||
$issuesAfter = detectIssues($fixResult['html']);
|
||||
|
||||
echo "ANTES: " . count($issues) . " problemas\n";
|
||||
echo "DESPUÉS: " . count($issuesAfter) . " problemas\n";
|
||||
echo "Cambios: {$fixResult['changes']}\n";
|
||||
|
||||
// Verificar integridad
|
||||
$before_ul = substr_count($row['post_content'], '<ul');
|
||||
$after_ul = substr_count($fixResult['html'], '<ul');
|
||||
$before_li = substr_count($row['post_content'], '<li');
|
||||
$after_li = substr_count($fixResult['html'], '<li');
|
||||
|
||||
echo "Tags <ul>: $before_ul → $after_ul " . ($before_ul === $after_ul ? "✓" : "⚠️") . "\n";
|
||||
echo "Tags <li>: $before_li → $after_li " . ($before_li === $after_li ? "✓" : "⚠️") . "\n";
|
||||
|
||||
if (count($issuesAfter) === 0) {
|
||||
echo "✅ CORRECCIÓN EXITOSA\n";
|
||||
} else {
|
||||
echo "⚠️ REQUIERE REVISIÓN\n";
|
||||
}
|
||||
echo "─────────────────────────────────\n\n";
|
||||
}
|
||||
}
|
||||
|
||||
} elseif ($mode === 'fix') {
|
||||
echo "MODO: CORRECCIÓN (GUARDANDO CAMBIOS)\n";
|
||||
echo "─────────────────────────────────\n\n";
|
||||
|
||||
$batch_size = 50;
|
||||
$offset = 0;
|
||||
$fixed_count = 0;
|
||||
$error_count = 0;
|
||||
|
||||
while ($offset < $total) {
|
||||
$query = "SELECT ID, post_content FROM wp_posts
|
||||
WHERE post_status = 'publish'
|
||||
AND post_type IN ('post', 'page')
|
||||
AND post_content IS NOT NULL
|
||||
AND post_content != ''
|
||||
ORDER BY ID LIMIT $batch_size OFFSET $offset";
|
||||
$result = $conn->query($query);
|
||||
|
||||
while ($row = $result->fetch_assoc()) {
|
||||
$issues = detectIssues($row['post_content']);
|
||||
|
||||
if (!empty($issues)) {
|
||||
$fixResult = fixMalformedLists($row['post_content']);
|
||||
|
||||
if ($fixResult['fixed']) {
|
||||
$stmt = $conn->prepare("UPDATE wp_posts SET post_content = ? WHERE ID = ?");
|
||||
$stmt->bind_param("si", $fixResult['html'], $row['ID']);
|
||||
|
||||
if ($stmt->execute()) {
|
||||
$fixed_count++;
|
||||
echo "[ID: {$row['ID']}] ✓ Corregido ({$fixResult['changes']} cambios)\n";
|
||||
} else {
|
||||
$error_count++;
|
||||
echo "[ID: {$row['ID']}] ✗ Error al guardar\n";
|
||||
}
|
||||
$stmt->close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$offset += $batch_size;
|
||||
|
||||
if ($offset % 500 == 0) {
|
||||
echo "Procesados: $offset/$total (corregidos: $fixed_count)\n";
|
||||
}
|
||||
}
|
||||
|
||||
echo "\n─────────────────────────────────\n";
|
||||
echo "RESUMEN:\n";
|
||||
echo " Posts corregidos: $fixed_count\n";
|
||||
echo " Errores: $error_count\n";
|
||||
}
|
||||
|
||||
$conn->close();
|
||||
echo "\n✓ Proceso completado.\n";
|
||||
@@ -1,307 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Script de Diagnóstico: Listas HTML Mal Formadas
|
||||
*
|
||||
* PROPÓSITO: Identificar posts con estructura de listas inválida
|
||||
* - <ul> conteniendo <ul> como hijo directo (en lugar de dentro de <li>)
|
||||
* - <ol> conteniendo <ol> como hijo directo
|
||||
*
|
||||
* BASE DE DATOS: preciosunitarios_seo
|
||||
* TABLA: datos_seo_pagina
|
||||
* CAMPO: html
|
||||
*
|
||||
* IMPORTANTE: Este script SOLO LEE, no modifica ningún dato.
|
||||
*
|
||||
* @package ROI_Theme
|
||||
* @since Phase 4.4 Accessibility
|
||||
*/
|
||||
|
||||
// Configuración de errores para debugging
|
||||
error_reporting(E_ALL);
|
||||
ini_set('display_errors', 1);
|
||||
ini_set('memory_limit', '512M');
|
||||
set_time_limit(300); // 5 minutos máximo
|
||||
|
||||
// Credenciales de base de datos (ajustar según servidor)
|
||||
$db_config = [
|
||||
'host' => 'localhost',
|
||||
'database' => 'preciosunitarios_seo',
|
||||
'username' => 'root', // Cambiar en producción
|
||||
'password' => '', // Cambiar en producción
|
||||
'charset' => 'utf8mb4'
|
||||
];
|
||||
|
||||
// Patrones regex para detectar listas mal formadas
|
||||
$malformed_patterns = [
|
||||
// <ul> seguido directamente de <ul> (sin estar dentro de <li>)
|
||||
'ul_direct_ul' => '/<ul[^>]*>\s*(?:<li[^>]*>.*?<\/li>\s*)*<ul/is',
|
||||
|
||||
// Patrón más específico: </li> seguido de <ul> (hermanos en lugar de anidados)
|
||||
'li_sibling_ul' => '/<\/li>\s*<ul[^>]*>/is',
|
||||
|
||||
// <ol> seguido directamente de <ol>
|
||||
'ol_direct_ol' => '/<ol[^>]*>\s*(?:<li[^>]*>.*?<\/li>\s*)*<ol/is',
|
||||
|
||||
// </li> seguido de <ol> (hermanos)
|
||||
'li_sibling_ol' => '/<\/li>\s*<ol[^>]*>/is',
|
||||
];
|
||||
|
||||
/**
|
||||
* Conectar a la base de datos
|
||||
*/
|
||||
function connectDatabase(array $config): ?mysqli {
|
||||
$conn = new mysqli(
|
||||
$config['host'],
|
||||
$config['username'],
|
||||
$config['password'],
|
||||
$config['database']
|
||||
);
|
||||
|
||||
if ($conn->connect_error) {
|
||||
echo "Error de conexión: " . $conn->connect_error . "\n";
|
||||
return null;
|
||||
}
|
||||
|
||||
$conn->set_charset($config['charset']);
|
||||
return $conn;
|
||||
}
|
||||
|
||||
/**
|
||||
* Analizar HTML en busca de listas mal formadas
|
||||
*/
|
||||
function analyzeMalformedLists(string $html, array $patterns): array {
|
||||
$issues = [];
|
||||
|
||||
foreach ($patterns as $pattern_name => $pattern) {
|
||||
if (preg_match_all($pattern, $html, $matches, PREG_OFFSET_CAPTURE)) {
|
||||
foreach ($matches[0] as $match) {
|
||||
$position = $match[1];
|
||||
$context = getContextAroundPosition($html, $position, 100);
|
||||
|
||||
$issues[] = [
|
||||
'type' => $pattern_name,
|
||||
'position' => $position,
|
||||
'context' => $context
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $issues;
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtener contexto alrededor de una posición
|
||||
*/
|
||||
function getContextAroundPosition(string $html, int $position, int $length = 100): string {
|
||||
$start = max(0, $position - $length);
|
||||
$end = min(strlen($html), $position + $length);
|
||||
|
||||
$context = substr($html, $start, $end - $start);
|
||||
|
||||
// Limpiar para mostrar
|
||||
$context = preg_replace('/\s+/', ' ', $context);
|
||||
$context = htmlspecialchars($context);
|
||||
|
||||
if ($start > 0) {
|
||||
$context = '...' . $context;
|
||||
}
|
||||
if ($end < strlen($html)) {
|
||||
$context .= '...';
|
||||
}
|
||||
|
||||
return $context;
|
||||
}
|
||||
|
||||
/**
|
||||
* Contar total de listas en el HTML
|
||||
*/
|
||||
function countListElements(string $html): array {
|
||||
$ul_count = preg_match_all('/<ul[^>]*>/i', $html);
|
||||
$ol_count = preg_match_all('/<ol[^>]*>/i', $html);
|
||||
$li_count = preg_match_all('/<li[^>]*>/i', $html);
|
||||
|
||||
return [
|
||||
'ul' => $ul_count,
|
||||
'ol' => $ol_count,
|
||||
'li' => $li_count
|
||||
];
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// EJECUCIÓN PRINCIPAL
|
||||
// ============================================
|
||||
|
||||
echo "==============================================\n";
|
||||
echo " DIAGNÓSTICO: Listas HTML Mal Formadas\n";
|
||||
echo " Base de datos: {$db_config['database']}\n";
|
||||
echo " Tabla: datos_seo_pagina\n";
|
||||
echo " Fecha: " . date('Y-m-d H:i:s') . "\n";
|
||||
echo "==============================================\n\n";
|
||||
|
||||
// Conectar
|
||||
$conn = connectDatabase($db_config);
|
||||
if (!$conn) {
|
||||
exit(1);
|
||||
}
|
||||
|
||||
echo "✓ Conexión establecida\n\n";
|
||||
|
||||
// Obtener estructura de la tabla
|
||||
echo "Verificando estructura de tabla...\n";
|
||||
$result = $conn->query("DESCRIBE datos_seo_pagina");
|
||||
if ($result) {
|
||||
echo "Columnas encontradas:\n";
|
||||
while ($row = $result->fetch_assoc()) {
|
||||
echo " - {$row['Field']} ({$row['Type']})\n";
|
||||
}
|
||||
echo "\n";
|
||||
}
|
||||
|
||||
// Contar registros totales
|
||||
$result = $conn->query("SELECT COUNT(*) as total FROM datos_seo_pagina WHERE html IS NOT NULL AND html != ''");
|
||||
$total = $result->fetch_assoc()['total'];
|
||||
echo "Total de registros con HTML: {$total}\n\n";
|
||||
|
||||
// Procesar en lotes
|
||||
$batch_size = 100;
|
||||
$offset = 0;
|
||||
$affected_posts = [];
|
||||
$total_issues = 0;
|
||||
$processed = 0;
|
||||
|
||||
echo "Iniciando análisis...\n";
|
||||
echo "─────────────────────────────────────────────\n";
|
||||
|
||||
while ($offset < $total) {
|
||||
$query = "SELECT id, page, html FROM datos_seo_pagina
|
||||
WHERE html IS NOT NULL AND html != ''
|
||||
ORDER BY id
|
||||
LIMIT {$batch_size} OFFSET {$offset}";
|
||||
|
||||
$result = $conn->query($query);
|
||||
|
||||
if (!$result) {
|
||||
echo "Error en consulta: " . $conn->error . "\n";
|
||||
break;
|
||||
}
|
||||
|
||||
while ($row = $result->fetch_assoc()) {
|
||||
$processed++;
|
||||
$id = $row['id'];
|
||||
$url = $row['page'] ?? 'N/A';
|
||||
$html = $row['html'];
|
||||
|
||||
$issues = analyzeMalformedLists($html, $malformed_patterns);
|
||||
|
||||
if (!empty($issues)) {
|
||||
$list_counts = countListElements($html);
|
||||
|
||||
$affected_posts[] = [
|
||||
'id' => $id,
|
||||
'url' => $url,
|
||||
'issues' => $issues,
|
||||
'list_counts' => $list_counts
|
||||
];
|
||||
|
||||
$total_issues += count($issues);
|
||||
|
||||
// Mostrar progreso para posts afectados
|
||||
echo "\n[ID: {$id}] " . count($issues) . " problema(s) encontrado(s)\n";
|
||||
echo "URL: {$url}\n";
|
||||
echo "Listas: UL={$list_counts['ul']}, OL={$list_counts['ol']}, LI={$list_counts['li']}\n";
|
||||
|
||||
foreach ($issues as $idx => $issue) {
|
||||
echo " Problema " . ($idx + 1) . ": {$issue['type']} (pos: {$issue['position']})\n";
|
||||
}
|
||||
}
|
||||
|
||||
// Mostrar progreso cada 500 registros
|
||||
if ($processed % 500 == 0) {
|
||||
echo "\rProcesados: {$processed}/{$total}...";
|
||||
}
|
||||
}
|
||||
|
||||
$offset += $batch_size;
|
||||
}
|
||||
|
||||
echo "\n\n";
|
||||
echo "==============================================\n";
|
||||
echo " RESUMEN DEL ANÁLISIS\n";
|
||||
echo "==============================================\n\n";
|
||||
|
||||
echo "Registros analizados: {$processed}\n";
|
||||
echo "Posts con problemas: " . count($affected_posts) . "\n";
|
||||
echo "Total de incidencias: {$total_issues}\n\n";
|
||||
|
||||
if (count($affected_posts) > 0) {
|
||||
echo "─────────────────────────────────────────────\n";
|
||||
echo "DETALLE DE POSTS AFECTADOS\n";
|
||||
echo "─────────────────────────────────────────────\n\n";
|
||||
|
||||
// Agrupar por tipo de problema
|
||||
$by_type = [];
|
||||
foreach ($affected_posts as $post) {
|
||||
foreach ($post['issues'] as $issue) {
|
||||
$type = $issue['type'];
|
||||
if (!isset($by_type[$type])) {
|
||||
$by_type[$type] = [];
|
||||
}
|
||||
$by_type[$type][] = $post['id'];
|
||||
}
|
||||
}
|
||||
|
||||
echo "Por tipo de problema:\n";
|
||||
foreach ($by_type as $type => $ids) {
|
||||
$unique_ids = array_unique($ids);
|
||||
echo " - {$type}: " . count($unique_ids) . " posts\n";
|
||||
}
|
||||
|
||||
echo "\n─────────────────────────────────────────────\n";
|
||||
echo "LISTA DE IDs AFECTADOS (para revisión manual)\n";
|
||||
echo "─────────────────────────────────────────────\n\n";
|
||||
|
||||
$ids_list = array_column($affected_posts, 'id');
|
||||
echo "IDs: " . implode(', ', $ids_list) . "\n";
|
||||
|
||||
// Generar archivo de reporte
|
||||
$report_file = __DIR__ . '/malformed-lists-report-' . date('Ymd-His') . '.json';
|
||||
$report_data = [
|
||||
'generated_at' => date('Y-m-d H:i:s'),
|
||||
'database' => $db_config['database'],
|
||||
'table' => 'datos_seo_pagina',
|
||||
'total_analyzed' => $processed,
|
||||
'total_affected' => count($affected_posts),
|
||||
'total_issues' => $total_issues,
|
||||
'by_type' => array_map(function($ids) {
|
||||
return array_values(array_unique($ids));
|
||||
}, $by_type),
|
||||
'affected_posts' => $affected_posts
|
||||
];
|
||||
|
||||
if (file_put_contents($report_file, json_encode($report_data, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE))) {
|
||||
echo "\n✓ Reporte JSON guardado en:\n {$report_file}\n";
|
||||
}
|
||||
|
||||
// Muestra de contexto para análisis
|
||||
echo "\n─────────────────────────────────────────────\n";
|
||||
echo "MUESTRA DE CONTEXTO (primeros 3 posts)\n";
|
||||
echo "─────────────────────────────────────────────\n\n";
|
||||
|
||||
$sample = array_slice($affected_posts, 0, 3);
|
||||
foreach ($sample as $post) {
|
||||
echo "POST ID: {$post['id']}\n";
|
||||
echo "URL: {$post['url']}\n";
|
||||
foreach ($post['issues'] as $idx => $issue) {
|
||||
echo " [{$issue['type']}]\n";
|
||||
echo " Contexto: {$issue['context']}\n\n";
|
||||
}
|
||||
echo "───────────────────────\n";
|
||||
}
|
||||
|
||||
} else {
|
||||
echo "✓ No se encontraron listas mal formadas.\n";
|
||||
}
|
||||
|
||||
$conn->close();
|
||||
echo "\n✓ Análisis completado.\n";
|
||||
@@ -1,91 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Script de PRUEBA - Muestra corrección propuesta sin aplicarla
|
||||
*
|
||||
* IMPORTANTE: Este script SOLO MUESTRA, no modifica nada.
|
||||
*/
|
||||
|
||||
$conn = new mysqli("localhost", "preciosunitarios_seo", "ACl%EEFd=V-Yvb??", "preciosunitarios_seo");
|
||||
$conn->set_charset("utf8mb4");
|
||||
|
||||
echo "========================================\n";
|
||||
echo "ANÁLISIS DE CORRECCIÓN PROPUESTA\n";
|
||||
echo "========================================\n\n";
|
||||
|
||||
// Patrón que encuentra: </li></ul><li>TEXTO</li><ul>
|
||||
// Este patrón captura:
|
||||
// - $1: </li> inicial (con espacios)
|
||||
// - $2: espacios entre </ul> y <li>
|
||||
// - $3: contenido del <li> (ej: <strong>Texto</strong>)
|
||||
// - $4: espacios entre </li> y <ul>
|
||||
|
||||
$pattern = '#(</li>\s*)</ul>(\s*)<li>(.*?)</li>(\s*)<ul>#is';
|
||||
$replacement = '$1<li>$3$4<ul>';
|
||||
|
||||
echo "PATRÓN A BUSCAR:\n";
|
||||
echo " </li>\\s*</ul>\\s*<li>CONTENIDO</li>\\s*<ul>\n\n";
|
||||
|
||||
echo "REEMPLAZO:\n";
|
||||
echo " </li><li>CONTENIDO<ul>\n\n";
|
||||
|
||||
// Obtener HTML del post ID 3
|
||||
$result = $conn->query("SELECT id, page, html FROM datos_seo_pagina WHERE id = 3");
|
||||
$row = $result->fetch_assoc();
|
||||
$html = $row["html"];
|
||||
$page = $row["page"];
|
||||
|
||||
echo "PROBANDO CON POST ID 3:\n";
|
||||
echo "URL: $page\n";
|
||||
echo "────────────────────────────\n\n";
|
||||
|
||||
// Encontrar todas las ocurrencias
|
||||
preg_match_all($pattern, $html, $matches, PREG_SET_ORDER | PREG_OFFSET_CAPTURE);
|
||||
|
||||
echo "Ocurrencias encontradas: " . count($matches) . "\n\n";
|
||||
|
||||
// Mostrar cada ocurrencia y su corrección propuesta
|
||||
foreach (array_slice($matches, 0, 3) as $idx => $match) {
|
||||
$full_match = $match[0][0];
|
||||
$position = $match[0][1];
|
||||
|
||||
echo "[$idx] Posición: $position\n";
|
||||
echo "ANTES:\n";
|
||||
echo htmlspecialchars($full_match) . "\n\n";
|
||||
|
||||
$fixed = preg_replace($pattern, $replacement, $full_match);
|
||||
echo "DESPUÉS:\n";
|
||||
echo htmlspecialchars($fixed) . "\n";
|
||||
echo "────────────────────────────\n\n";
|
||||
}
|
||||
|
||||
// Aplicar corrección en memoria y contar diferencia
|
||||
$html_fixed = preg_replace($pattern, $replacement, $html);
|
||||
|
||||
$before = preg_match_all($pattern, $html);
|
||||
$after = preg_match_all($pattern, $html_fixed);
|
||||
|
||||
echo "========================================\n";
|
||||
echo "RESUMEN DE CORRECCIÓN (sin aplicar):\n";
|
||||
echo "========================================\n";
|
||||
echo "Ocurrencias ANTES: $before\n";
|
||||
echo "Ocurrencias DESPUÉS: $after\n";
|
||||
echo "Reducción: " . ($before - $after) . "\n\n";
|
||||
|
||||
// Verificar que la estructura es válida después de la corrección
|
||||
$ul_count_before = substr_count($html, '<ul');
|
||||
$ul_count_after = substr_count($html_fixed, '<ul');
|
||||
echo "Tags <ul> antes: $ul_count_before\n";
|
||||
echo "Tags <ul> después: $ul_count_after\n";
|
||||
|
||||
$li_count_before = substr_count($html, '<li');
|
||||
$li_count_after = substr_count($html_fixed, '<li');
|
||||
echo "Tags <li> antes: $li_count_before\n";
|
||||
echo "Tags <li> después: $li_count_after\n";
|
||||
|
||||
echo "\n========================================\n";
|
||||
echo "NOTA: Este patrón elimina el </ul> prematuro\n";
|
||||
echo "pero NO agrega el </li> faltante al final.\n";
|
||||
echo "Se necesita un segundo paso para balancear.\n";
|
||||
echo "========================================\n";
|
||||
|
||||
$conn->close();
|
||||
@@ -1,187 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Prueba corrección en casos específicos variados
|
||||
*/
|
||||
|
||||
$conn = new mysqli("localhost", "preciosunitarios_seo", "ACl%EEFd=V-Yvb??", "preciosunitarios_seo");
|
||||
$conn->set_charset("utf8mb4");
|
||||
|
||||
// IDs a probar (casos variados)
|
||||
$test_ids = [20, 23, 65, 377, 98, 107, 144];
|
||||
|
||||
function detectIssues($html) {
|
||||
$issues = [];
|
||||
libxml_use_internal_errors(true);
|
||||
$doc = new DOMDocument("1.0", "UTF-8");
|
||||
$doc->loadHTML('<?xml encoding="UTF-8"><div id="w">' . $html . '</div>', LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD);
|
||||
libxml_clear_errors();
|
||||
|
||||
$validChildren = ["li", "script", "template"];
|
||||
foreach (["ul", "ol"] as $tag) {
|
||||
foreach ($doc->getElementsByTagName($tag) as $list) {
|
||||
foreach ($list->childNodes as $child) {
|
||||
if ($child->nodeType === XML_ELEMENT_NODE) {
|
||||
$childTag = strtolower($child->nodeName);
|
||||
if (!in_array($childTag, $validChildren)) {
|
||||
$issues[] = "<$tag> contiene <$childTag>";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return $issues;
|
||||
}
|
||||
|
||||
function fixMalformedLists($html) {
|
||||
$result = ['fixed' => false, 'html' => $html, 'changes' => 0];
|
||||
|
||||
libxml_use_internal_errors(true);
|
||||
$doc = new DOMDocument("1.0", "UTF-8");
|
||||
$doc->loadHTML('<?xml encoding="UTF-8"><div id="w">' . $html . '</div>', 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);
|
||||
$changes++;
|
||||
} else {
|
||||
$newLi = $doc->createElement('li');
|
||||
$list->insertBefore($newLi, $node);
|
||||
$newLi->appendChild($node);
|
||||
$changes++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($changes > 0) {
|
||||
$wrapper = $doc->getElementById('w');
|
||||
if ($wrapper) {
|
||||
$innerHTML = '';
|
||||
foreach ($wrapper->childNodes as $child) {
|
||||
$innerHTML .= $doc->saveHTML($child);
|
||||
}
|
||||
$result['html'] = $innerHTML;
|
||||
$result['fixed'] = true;
|
||||
$result['changes'] = $changes;
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
echo "=====================================================\n";
|
||||
echo " PRUEBA DE CORRECCIÓN EN CASOS VARIADOS\n";
|
||||
echo "=====================================================\n\n";
|
||||
|
||||
$ids_str = implode(',', $test_ids);
|
||||
$query = "SELECT id, page, html FROM datos_seo_pagina WHERE id IN ($ids_str)";
|
||||
$result = $conn->query($query);
|
||||
|
||||
$all_passed = true;
|
||||
|
||||
while ($row = $result->fetch_assoc()) {
|
||||
$id = $row['id'];
|
||||
$url = $row['page'];
|
||||
$html = $row['html'];
|
||||
|
||||
echo "─────────────────────────────────────────────────\n";
|
||||
echo "POST ID: $id\n";
|
||||
echo "URL: $url\n\n";
|
||||
|
||||
// Detectar problemas antes
|
||||
$issues_before = detectIssues($html);
|
||||
echo "ANTES:\n";
|
||||
echo " Problemas: " . count($issues_before) . "\n";
|
||||
$unique_types = array_unique($issues_before);
|
||||
foreach ($unique_types as $type) {
|
||||
echo " - $type\n";
|
||||
}
|
||||
|
||||
// Aplicar corrección
|
||||
$fixResult = fixMalformedLists($html);
|
||||
|
||||
// Detectar problemas después
|
||||
$issues_after = detectIssues($fixResult['html']);
|
||||
|
||||
echo "\nDESPUÉS:\n";
|
||||
echo " Cambios aplicados: {$fixResult['changes']}\n";
|
||||
echo " Problemas restantes: " . count($issues_after) . "\n";
|
||||
|
||||
if (count($issues_after) > 0) {
|
||||
echo " ⚠️ Problemas NO resueltos:\n";
|
||||
foreach (array_unique($issues_after) as $type) {
|
||||
echo " - $type\n";
|
||||
}
|
||||
$all_passed = false;
|
||||
}
|
||||
|
||||
// Verificar integridad del HTML
|
||||
$tags_before = [
|
||||
'ul' => substr_count($html, '<ul'),
|
||||
'ol' => substr_count($html, '<ol'),
|
||||
'li' => substr_count($html, '<li'),
|
||||
];
|
||||
$tags_after = [
|
||||
'ul' => substr_count($fixResult['html'], '<ul'),
|
||||
'ol' => substr_count($fixResult['html'], '<ol'),
|
||||
'li' => substr_count($fixResult['html'], '<li'),
|
||||
];
|
||||
|
||||
echo "\nINTEGRIDAD DE TAGS:\n";
|
||||
echo " <ul>: {$tags_before['ul']} → {$tags_after['ul']} ";
|
||||
echo ($tags_before['ul'] === $tags_after['ul'] ? "✓" : "⚠️ CAMBIÓ") . "\n";
|
||||
echo " <ol>: {$tags_before['ol']} → {$tags_after['ol']} ";
|
||||
echo ($tags_before['ol'] === $tags_after['ol'] ? "✓" : "⚠️ CAMBIÓ") . "\n";
|
||||
echo " <li>: {$tags_before['li']} → {$tags_after['li']} ";
|
||||
echo ($tags_before['li'] === $tags_after['li'] ? "✓" : "⚠️ CAMBIÓ") . "\n";
|
||||
|
||||
// Resultado
|
||||
if (count($issues_after) === 0 &&
|
||||
$tags_before['ul'] === $tags_after['ul'] &&
|
||||
$tags_before['ol'] === $tags_after['ol']) {
|
||||
echo "\n✅ RESULTADO: CORRECCIÓN EXITOSA\n";
|
||||
} else {
|
||||
echo "\n❌ RESULTADO: REQUIERE REVISIÓN\n";
|
||||
$all_passed = false;
|
||||
}
|
||||
}
|
||||
|
||||
echo "\n=====================================================\n";
|
||||
if ($all_passed) {
|
||||
echo "✅ TODOS LOS CASOS PASARON LA PRUEBA\n";
|
||||
} else {
|
||||
echo "⚠️ ALGUNOS CASOS REQUIEREN REVISIÓN\n";
|
||||
}
|
||||
echo "=====================================================\n";
|
||||
|
||||
$conn->close();
|
||||
@@ -1,347 +0,0 @@
|
||||
<?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";
|
||||
@@ -1,697 +0,0 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace ROITheme\Shared\Infrastructure\Ui;
|
||||
|
||||
use ROITheme\Shared\Domain\Entities\Component;
|
||||
use ROITheme\Shared\Domain\Contracts\FormBuilderInterface;
|
||||
|
||||
/**
|
||||
* TopNotificationBarFormBuilder - Construye formulario de configuración
|
||||
*
|
||||
* RESPONSABILIDAD: Generar formulario HTML del admin para Top Notification Bar
|
||||
*
|
||||
* CARACTERÍSTICAS:
|
||||
* - 3 secciones: Visibilidad, Contenido, Estilos
|
||||
* - 19 campos configurables
|
||||
* - Lógica condicional (data-conditional-field)
|
||||
* - WordPress Media Library integration
|
||||
* - Vista previa en tiempo real
|
||||
*
|
||||
* @package ROITheme\Shared\Infrastructure\Ui
|
||||
*/
|
||||
final class TopNotificationBarFormBuilder implements FormBuilderInterface
|
||||
{
|
||||
public function build(Component $component): string
|
||||
{
|
||||
$data = $component->getData();
|
||||
$componentId = $component->getName();
|
||||
|
||||
$html = '<div class="roi-form-builder roi-top-notification-bar-form">';
|
||||
|
||||
// Sección de Visibilidad
|
||||
$html .= $this->buildVisibilitySection($data, $componentId);
|
||||
|
||||
// Sección de Contenido
|
||||
$html .= $this->buildContentSection($data, $componentId);
|
||||
|
||||
// Sección de Estilos
|
||||
$html .= $this->buildStylesSection($data, $componentId);
|
||||
|
||||
// Vista previa
|
||||
$html .= $this->buildPreviewSection($data);
|
||||
|
||||
$html .= '</div>';
|
||||
|
||||
// Agregar scripts de formulario
|
||||
$html .= $this->buildFormScripts($componentId);
|
||||
|
||||
return $html;
|
||||
}
|
||||
|
||||
private function buildVisibilitySection(array $data, string $componentId): string
|
||||
{
|
||||
$html = '<div class="roi-form-section" data-section="visibility">';
|
||||
$html .= '<h3 class="roi-form-section-title">Visibilidad</h3>';
|
||||
$html .= '<div class="roi-form-section-content">';
|
||||
|
||||
// Is Enabled
|
||||
$isEnabled = $data['visibility']['is_enabled'] ?? true;
|
||||
$html .= $this->buildToggle(
|
||||
'is_enabled',
|
||||
'Mostrar barra de notificación',
|
||||
$isEnabled,
|
||||
$componentId,
|
||||
'Activa o desactiva la barra de notificación superior'
|
||||
);
|
||||
|
||||
// Show On Pages
|
||||
$showOn = $data['visibility']['show_on_pages'] ?? 'all';
|
||||
$html .= $this->buildSelect(
|
||||
'show_on_pages',
|
||||
'Mostrar en',
|
||||
$showOn,
|
||||
[
|
||||
'all' => 'Todas las páginas',
|
||||
'home' => 'Solo página de inicio',
|
||||
'posts' => 'Solo posts individuales',
|
||||
'pages' => 'Solo páginas',
|
||||
'custom' => 'Páginas específicas'
|
||||
],
|
||||
$componentId,
|
||||
'Define en qué páginas se mostrará la barra'
|
||||
);
|
||||
|
||||
// Custom Page IDs
|
||||
$customPageIds = $data['visibility']['custom_page_ids'] ?? '';
|
||||
$html .= $this->buildTextField(
|
||||
'custom_page_ids',
|
||||
'IDs de páginas específicas',
|
||||
$customPageIds,
|
||||
$componentId,
|
||||
'IDs de páginas separados por comas',
|
||||
'Ej: 1,5,10',
|
||||
['data-conditional-field' => 'show_on_pages', 'data-conditional-value' => 'custom']
|
||||
);
|
||||
|
||||
// Hide On Mobile
|
||||
$hideOnMobile = $data['visibility']['hide_on_mobile'] ?? false;
|
||||
$html .= $this->buildToggle(
|
||||
'hide_on_mobile',
|
||||
'Ocultar en dispositivos móviles',
|
||||
$hideOnMobile,
|
||||
$componentId,
|
||||
'Oculta la barra en pantallas menores a 768px'
|
||||
);
|
||||
|
||||
// Is Dismissible
|
||||
$isDismissible = $data['visibility']['is_dismissible'] ?? false;
|
||||
$html .= $this->buildToggle(
|
||||
'is_dismissible',
|
||||
'Permitir cerrar',
|
||||
$isDismissible,
|
||||
$componentId,
|
||||
'Agrega botón X para que el usuario pueda cerrar la barra'
|
||||
);
|
||||
|
||||
// Dismissible Cookie Days
|
||||
$cookieDays = $data['visibility']['dismissible_cookie_days'] ?? 7;
|
||||
$html .= $this->buildNumberField(
|
||||
'dismissible_cookie_days',
|
||||
'Días antes de volver a mostrar',
|
||||
$cookieDays,
|
||||
$componentId,
|
||||
'Días que permanece oculta después de cerrarla',
|
||||
1,
|
||||
365,
|
||||
['data-conditional-field' => 'is_dismissible', 'data-conditional-value' => 'true']
|
||||
);
|
||||
|
||||
$html .= '</div>';
|
||||
$html .= '</div>';
|
||||
|
||||
return $html;
|
||||
}
|
||||
|
||||
private function buildContentSection(array $data, string $componentId): string
|
||||
{
|
||||
$html = '<div class="roi-form-section" data-section="content">';
|
||||
$html .= '<h3 class="roi-form-section-title">Contenido</h3>';
|
||||
$html .= '<div class="roi-form-section-content">';
|
||||
|
||||
// Icon Type
|
||||
$iconType = $data['content']['icon_type'] ?? 'bootstrap';
|
||||
$html .= $this->buildSelect(
|
||||
'icon_type',
|
||||
'Tipo de ícono',
|
||||
$iconType,
|
||||
[
|
||||
'bootstrap' => 'Bootstrap Icons',
|
||||
'custom' => 'Imagen personalizada',
|
||||
'none' => 'Sin ícono'
|
||||
],
|
||||
$componentId,
|
||||
'Selecciona el tipo de ícono a mostrar'
|
||||
);
|
||||
|
||||
// Bootstrap Icon
|
||||
$bootstrapIcon = $data['content']['bootstrap_icon'] ?? 'bi-megaphone-fill';
|
||||
$html .= $this->buildTextField(
|
||||
'bootstrap_icon',
|
||||
'Clase de ícono Bootstrap',
|
||||
$bootstrapIcon,
|
||||
$componentId,
|
||||
'Nombre de la clase del ícono sin el prefijo \'bi\' (ej: megaphone-fill)',
|
||||
'Ej: bi-megaphone-fill',
|
||||
['data-conditional-field' => 'icon_type', 'data-conditional-value' => 'bootstrap']
|
||||
);
|
||||
|
||||
// Custom Icon URL
|
||||
$customIconUrl = $data['content']['custom_icon_url'] ?? '';
|
||||
$html .= $this->buildMediaField(
|
||||
'custom_icon_url',
|
||||
'Imagen personalizada',
|
||||
$customIconUrl,
|
||||
$componentId,
|
||||
'Sube una imagen personalizada (recomendado: PNG 24x24px)',
|
||||
['data-conditional-field' => 'icon_type', 'data-conditional-value' => 'custom']
|
||||
);
|
||||
|
||||
// Announcement Label
|
||||
$announcementLabel = $data['content']['announcement_label'] ?? 'Nuevo:';
|
||||
$html .= $this->buildTextField(
|
||||
'announcement_label',
|
||||
'Etiqueta del anuncio',
|
||||
$announcementLabel,
|
||||
$componentId,
|
||||
'Texto destacado en negrita antes del mensaje',
|
||||
'Ej: Nuevo:, Importante:, Aviso:'
|
||||
);
|
||||
|
||||
// Announcement Text
|
||||
$announcementText = $data['content']['announcement_text'] ?? 'Accede a más de 200,000 Análisis de Precios Unitarios actualizados para 2025.';
|
||||
$html .= $this->buildTextArea(
|
||||
'announcement_text',
|
||||
'Texto del anuncio',
|
||||
$announcementText,
|
||||
$componentId,
|
||||
'Mensaje principal del anuncio (máximo 200 caracteres)',
|
||||
3
|
||||
);
|
||||
|
||||
// Link Enabled
|
||||
$linkEnabled = $data['content']['link_enabled'] ?? true;
|
||||
$html .= $this->buildToggle(
|
||||
'link_enabled',
|
||||
'Mostrar enlace',
|
||||
$linkEnabled,
|
||||
$componentId,
|
||||
'Activa o desactiva el enlace de acción'
|
||||
);
|
||||
|
||||
// Link Text
|
||||
$linkText = $data['content']['link_text'] ?? 'Ver Catálogo';
|
||||
$html .= $this->buildTextField(
|
||||
'link_text',
|
||||
'Texto del enlace',
|
||||
$linkText,
|
||||
$componentId,
|
||||
'Texto del enlace de acción',
|
||||
'',
|
||||
['data-conditional-field' => 'link_enabled', 'data-conditional-value' => 'true']
|
||||
);
|
||||
|
||||
// Link URL
|
||||
$linkUrl = $data['content']['link_url'] ?? '#';
|
||||
$html .= $this->buildUrlField(
|
||||
'link_url',
|
||||
'URL del enlace',
|
||||
$linkUrl,
|
||||
$componentId,
|
||||
'URL de destino del enlace',
|
||||
'https://',
|
||||
['data-conditional-field' => 'link_enabled', 'data-conditional-value' => 'true']
|
||||
);
|
||||
|
||||
// Link Target
|
||||
$linkTarget = $data['content']['link_target'] ?? '_self';
|
||||
$html .= $this->buildSelect(
|
||||
'link_target',
|
||||
'Abrir enlace en',
|
||||
$linkTarget,
|
||||
[
|
||||
'_self' => 'Misma ventana',
|
||||
'_blank' => 'Nueva ventana'
|
||||
],
|
||||
$componentId,
|
||||
'Define cómo se abrirá el enlace',
|
||||
['data-conditional-field' => 'link_enabled', 'data-conditional-value' => 'true']
|
||||
);
|
||||
|
||||
$html .= '</div>';
|
||||
$html .= '</div>';
|
||||
|
||||
return $html;
|
||||
}
|
||||
|
||||
private function buildStylesSection(array $data, string $componentId): string
|
||||
{
|
||||
$html = '<div class="roi-form-section" data-section="styles">';
|
||||
$html .= '<h3 class="roi-form-section-title">Estilos</h3>';
|
||||
$html .= '<div class="roi-form-section-content">';
|
||||
|
||||
// Background Color
|
||||
$bgColor = $data['styles']['background_color'] ?? '#FF8600';
|
||||
$html .= $this->buildColorField(
|
||||
'background_color',
|
||||
'Color de fondo',
|
||||
$bgColor,
|
||||
$componentId,
|
||||
'Color de fondo de la barra (por defecto: orange primary)'
|
||||
);
|
||||
|
||||
// Text Color
|
||||
$textColor = $data['styles']['text_color'] ?? '#FFFFFF';
|
||||
$html .= $this->buildColorField(
|
||||
'text_color',
|
||||
'Color del texto',
|
||||
$textColor,
|
||||
$componentId,
|
||||
'Color del texto del anuncio'
|
||||
);
|
||||
|
||||
// Link Color
|
||||
$linkColor = $data['styles']['link_color'] ?? '#FFFFFF';
|
||||
$html .= $this->buildColorField(
|
||||
'link_color',
|
||||
'Color del enlace',
|
||||
$linkColor,
|
||||
$componentId,
|
||||
'Color del enlace de acción'
|
||||
);
|
||||
|
||||
// Font Size
|
||||
$fontSize = $data['styles']['font_size'] ?? 'small';
|
||||
$html .= $this->buildSelect(
|
||||
'font_size',
|
||||
'Tamaño de fuente',
|
||||
$fontSize,
|
||||
[
|
||||
'extra-small' => 'Muy pequeño (0.75rem)',
|
||||
'small' => 'Pequeño (0.875rem)',
|
||||
'normal' => 'Normal (1rem)',
|
||||
'large' => 'Grande (1.125rem)'
|
||||
],
|
||||
$componentId,
|
||||
'Tamaño del texto del anuncio'
|
||||
);
|
||||
|
||||
// Padding Vertical
|
||||
$padding = $data['styles']['padding_vertical'] ?? 'normal';
|
||||
$html .= $this->buildSelect(
|
||||
'padding_vertical',
|
||||
'Padding vertical',
|
||||
$padding,
|
||||
[
|
||||
'compact' => 'Compacto (0.5rem)',
|
||||
'normal' => 'Normal (0.75rem)',
|
||||
'spacious' => 'Espacioso (1rem)'
|
||||
],
|
||||
$componentId,
|
||||
'Espaciado vertical interno de la barra'
|
||||
);
|
||||
|
||||
// Text Alignment
|
||||
$alignment = $data['styles']['text_alignment'] ?? 'center';
|
||||
$html .= $this->buildSelect(
|
||||
'text_alignment',
|
||||
'Alineación del texto',
|
||||
$alignment,
|
||||
[
|
||||
'left' => 'Izquierda',
|
||||
'center' => 'Centro',
|
||||
'right' => 'Derecha'
|
||||
],
|
||||
$componentId,
|
||||
'Alineación del contenido de la barra'
|
||||
);
|
||||
|
||||
// Animation Enabled
|
||||
$animationEnabled = $data['styles']['animation_enabled'] ?? false;
|
||||
$html .= $this->buildToggle(
|
||||
'animation_enabled',
|
||||
'Activar animación',
|
||||
$animationEnabled,
|
||||
$componentId,
|
||||
'Activa animación de entrada al cargar la página'
|
||||
);
|
||||
|
||||
// Animation Type
|
||||
$animationType = $data['styles']['animation_type'] ?? 'slide-down';
|
||||
$html .= $this->buildSelect(
|
||||
'animation_type',
|
||||
'Tipo de animación',
|
||||
$animationType,
|
||||
[
|
||||
'slide-down' => 'Deslizar desde arriba',
|
||||
'fade-in' => 'Aparecer gradualmente'
|
||||
],
|
||||
$componentId,
|
||||
'Tipo de animación de entrada',
|
||||
['data-conditional-field' => 'animation_enabled', 'data-conditional-value' => 'true']
|
||||
);
|
||||
|
||||
$html .= '</div>';
|
||||
$html .= '</div>';
|
||||
|
||||
return $html;
|
||||
}
|
||||
|
||||
private function buildPreviewSection(array $data): string
|
||||
{
|
||||
$html = '<div class="roi-form-section roi-preview-section">';
|
||||
$html .= '<h3 class="roi-form-section-title">Vista Previa</h3>';
|
||||
$html .= '<div class="roi-form-section-content">';
|
||||
$html .= '<div id="roi-component-preview" class="border rounded p-3 bg-light">';
|
||||
$html .= '<p class="text-muted">La vista previa se actualizará automáticamente al modificar los campos.</p>';
|
||||
$html .= '</div>';
|
||||
$html .= '</div>';
|
||||
$html .= '</div>';
|
||||
|
||||
return $html;
|
||||
}
|
||||
|
||||
private function buildToggle(string $name, string $label, bool $value, string $componentId, string $description = ''): string
|
||||
{
|
||||
$fieldId = "roi_{$componentId}_{$name}";
|
||||
$checked = $value ? 'checked' : '';
|
||||
|
||||
$html = '<div class="roi-form-field roi-form-field-toggle mb-3">';
|
||||
$html .= '<div class="form-check form-switch">';
|
||||
$html .= sprintf(
|
||||
'<input type="checkbox" class="form-check-input" id="%s" name="roi_component[%s][%s]" value="1" %s>',
|
||||
esc_attr($fieldId),
|
||||
esc_attr($componentId),
|
||||
esc_attr($name),
|
||||
$checked
|
||||
);
|
||||
$html .= sprintf('<label class="form-check-label" for="%s">%s</label>', esc_attr($fieldId), esc_html($label));
|
||||
$html .= '</div>';
|
||||
if (!empty($description)) {
|
||||
$html .= sprintf('<small class="form-text text-muted">%s</small>', esc_html($description));
|
||||
}
|
||||
$html .= '</div>';
|
||||
|
||||
return $html;
|
||||
}
|
||||
|
||||
private function buildTextField(string $name, string $label, string $value, string $componentId, string $description = '', string $placeholder = '', array $attrs = []): string
|
||||
{
|
||||
$fieldId = "roi_{$componentId}_{$name}";
|
||||
|
||||
$html = '<div class="roi-form-field roi-form-field-text mb-3">';
|
||||
$html .= sprintf('<label for="%s" class="form-label">%s</label>', esc_attr($fieldId), esc_html($label));
|
||||
|
||||
$attrString = $this->buildAttributesString($attrs);
|
||||
|
||||
$html .= sprintf(
|
||||
'<input type="text" class="form-control" id="%s" name="roi_component[%s][%s]" value="%s" placeholder="%s"%s>',
|
||||
esc_attr($fieldId),
|
||||
esc_attr($componentId),
|
||||
esc_attr($name),
|
||||
esc_attr($value),
|
||||
esc_attr($placeholder),
|
||||
$attrString
|
||||
);
|
||||
|
||||
if (!empty($description)) {
|
||||
$html .= sprintf('<small class="form-text text-muted">%s</small>', esc_html($description));
|
||||
}
|
||||
$html .= '</div>';
|
||||
|
||||
return $html;
|
||||
}
|
||||
|
||||
private function buildTextArea(string $name, string $label, string $value, string $componentId, string $description = '', int $rows = 3, array $attrs = []): string
|
||||
{
|
||||
$fieldId = "roi_{$componentId}_{$name}";
|
||||
|
||||
$html = '<div class="roi-form-field roi-form-field-textarea mb-3">';
|
||||
$html .= sprintf('<label for="%s" class="form-label">%s</label>', esc_attr($fieldId), esc_html($label));
|
||||
|
||||
$attrString = $this->buildAttributesString($attrs);
|
||||
|
||||
$html .= sprintf(
|
||||
'<textarea class="form-control" id="%s" name="roi_component[%s][%s]" rows="%d"%s>%s</textarea>',
|
||||
esc_attr($fieldId),
|
||||
esc_attr($componentId),
|
||||
esc_attr($name),
|
||||
$rows,
|
||||
$attrString,
|
||||
esc_textarea($value)
|
||||
);
|
||||
|
||||
if (!empty($description)) {
|
||||
$html .= sprintf('<small class="form-text text-muted">%s</small>', esc_html($description));
|
||||
}
|
||||
$html .= '</div>';
|
||||
|
||||
return $html;
|
||||
}
|
||||
|
||||
private function buildSelect(string $name, string $label, string $value, array $options, string $componentId, string $description = '', array $attrs = []): string
|
||||
{
|
||||
$fieldId = "roi_{$componentId}_{$name}";
|
||||
|
||||
$html = '<div class="roi-form-field roi-form-field-select mb-3">';
|
||||
$html .= sprintf('<label for="%s" class="form-label">%s</label>', esc_attr($fieldId), esc_html($label));
|
||||
|
||||
$attrString = $this->buildAttributesString($attrs);
|
||||
|
||||
$html .= sprintf(
|
||||
'<select class="form-select" id="%s" name="roi_component[%s][%s]"%s>',
|
||||
esc_attr($fieldId),
|
||||
esc_attr($componentId),
|
||||
esc_attr($name),
|
||||
$attrString
|
||||
);
|
||||
|
||||
foreach ($options as $optValue => $optLabel) {
|
||||
$selected = ($value === $optValue) ? 'selected' : '';
|
||||
$html .= sprintf(
|
||||
'<option value="%s" %s>%s</option>',
|
||||
esc_attr($optValue),
|
||||
$selected,
|
||||
esc_html($optLabel)
|
||||
);
|
||||
}
|
||||
|
||||
$html .= '</select>';
|
||||
|
||||
if (!empty($description)) {
|
||||
$html .= sprintf('<small class="form-text text-muted">%s</small>', esc_html($description));
|
||||
}
|
||||
$html .= '</div>';
|
||||
|
||||
return $html;
|
||||
}
|
||||
|
||||
private function buildNumberField(string $name, string $label, $value, string $componentId, string $description = '', int $min = null, int $max = null, array $attrs = []): string
|
||||
{
|
||||
$fieldId = "roi_{$componentId}_{$name}";
|
||||
|
||||
$html = '<div class="roi-form-field roi-form-field-number mb-3">';
|
||||
$html .= sprintf('<label for="%s" class="form-label">%s</label>', esc_attr($fieldId), esc_html($label));
|
||||
|
||||
$attrs['type'] = 'number';
|
||||
if ($min !== null) {
|
||||
$attrs['min'] = $min;
|
||||
}
|
||||
if ($max !== null) {
|
||||
$attrs['max'] = $max;
|
||||
}
|
||||
|
||||
$attrString = $this->buildAttributesString($attrs);
|
||||
|
||||
$html .= sprintf(
|
||||
'<input class="form-control" id="%s" name="roi_component[%s][%s]" value="%s"%s>',
|
||||
esc_attr($fieldId),
|
||||
esc_attr($componentId),
|
||||
esc_attr($name),
|
||||
esc_attr($value),
|
||||
$attrString
|
||||
);
|
||||
|
||||
if (!empty($description)) {
|
||||
$html .= sprintf('<small class="form-text text-muted">%s</small>', esc_html($description));
|
||||
}
|
||||
$html .= '</div>';
|
||||
|
||||
return $html;
|
||||
}
|
||||
|
||||
private function buildUrlField(string $name, string $label, string $value, string $componentId, string $description = '', string $placeholder = '', array $attrs = []): string
|
||||
{
|
||||
$attrs['type'] = 'url';
|
||||
return $this->buildTextField($name, $label, $value, $componentId, $description, $placeholder, $attrs);
|
||||
}
|
||||
|
||||
private function buildColorField(string $name, string $label, string $value, string $componentId, string $description = ''): string
|
||||
{
|
||||
$fieldId = "roi_{$componentId}_{$name}";
|
||||
|
||||
$html = '<div class="roi-form-field roi-form-field-color mb-3">';
|
||||
$html .= sprintf('<label for="%s" class="form-label">%s</label>', esc_attr($fieldId), esc_html($label));
|
||||
$html .= '<div class="input-group">';
|
||||
$html .= sprintf(
|
||||
'<input type="color" class="form-control form-control-color" id="%s" name="roi_component[%s][%s]" value="%s">',
|
||||
esc_attr($fieldId),
|
||||
esc_attr($componentId),
|
||||
esc_attr($name),
|
||||
esc_attr($value)
|
||||
);
|
||||
$html .= sprintf(
|
||||
'<input type="text" class="form-control" value="%s" readonly>',
|
||||
esc_attr($value)
|
||||
);
|
||||
$html .= '</div>';
|
||||
if (!empty($description)) {
|
||||
$html .= sprintf('<small class="form-text text-muted">%s</small>', esc_html($description));
|
||||
}
|
||||
$html .= '</div>';
|
||||
|
||||
return $html;
|
||||
}
|
||||
|
||||
private function buildMediaField(string $name, string $label, string $value, string $componentId, string $description = '', array $attrs = []): string
|
||||
{
|
||||
$fieldId = "roi_{$componentId}_{$name}";
|
||||
|
||||
$html = '<div class="roi-form-field roi-form-field-media mb-3">';
|
||||
$html .= sprintf('<label for="%s" class="form-label">%s</label>', esc_attr($fieldId), esc_html($label));
|
||||
$html .= '<div class="input-group">';
|
||||
|
||||
$attrString = $this->buildAttributesString($attrs);
|
||||
|
||||
$html .= sprintf(
|
||||
'<input type="text" class="form-control" id="%s" name="roi_component[%s][%s]" value="%s" readonly%s>',
|
||||
esc_attr($fieldId),
|
||||
esc_attr($componentId),
|
||||
esc_attr($name),
|
||||
esc_attr($value),
|
||||
$attrString
|
||||
);
|
||||
$html .= sprintf(
|
||||
'<button type="button" class="btn btn-primary roi-media-upload-btn" data-target="%s">Seleccionar</button>',
|
||||
esc_attr($fieldId)
|
||||
);
|
||||
$html .= '</div>';
|
||||
|
||||
if (!empty($value)) {
|
||||
$html .= sprintf('<div class="mt-2"><img src="%s" alt="Preview" style="max-width: 100px; height: auto;"></div>', esc_url($value));
|
||||
}
|
||||
|
||||
if (!empty($description)) {
|
||||
$html .= sprintf('<small class="form-text text-muted">%s</small>', esc_html($description));
|
||||
}
|
||||
$html .= '</div>';
|
||||
|
||||
return $html;
|
||||
}
|
||||
|
||||
private function buildAttributesString(array $attrs): string
|
||||
{
|
||||
$attrString = '';
|
||||
foreach ($attrs as $key => $value) {
|
||||
$attrString .= sprintf(' %s="%s"', esc_attr($key), esc_attr($value));
|
||||
}
|
||||
return $attrString;
|
||||
}
|
||||
|
||||
private function buildFormScripts(string $componentId): string
|
||||
{
|
||||
return <<<SCRIPT
|
||||
<script>
|
||||
(function($) {
|
||||
'use strict';
|
||||
|
||||
$(document).ready(function() {
|
||||
// Conditional logic
|
||||
$('[data-conditional-field]').each(function() {
|
||||
const field = $(this);
|
||||
const targetFieldName = field.data('conditional-field');
|
||||
const targetValue = field.data('conditional-value');
|
||||
const targetField = $('[name*="[' + targetFieldName + ']"]');
|
||||
|
||||
function updateVisibility() {
|
||||
let currentValue;
|
||||
if (targetField.is(':checkbox')) {
|
||||
currentValue = targetField.is(':checked') ? 'true' : 'false';
|
||||
} else {
|
||||
currentValue = targetField.val();
|
||||
}
|
||||
|
||||
if (currentValue === targetValue) {
|
||||
field.closest('.roi-form-field').show();
|
||||
} else {
|
||||
field.closest('.roi-form-field').hide();
|
||||
}
|
||||
}
|
||||
|
||||
targetField.on('change', updateVisibility);
|
||||
updateVisibility();
|
||||
});
|
||||
|
||||
// Media upload
|
||||
$('.roi-media-upload-btn').on('click', function(e) {
|
||||
e.preventDefault();
|
||||
const button = $(this);
|
||||
const targetId = button.data('target');
|
||||
const targetField = $('#' + targetId);
|
||||
|
||||
const mediaUploader = wp.media({
|
||||
title: 'Seleccionar imagen',
|
||||
button: { text: 'Usar esta imagen' },
|
||||
multiple: false
|
||||
});
|
||||
|
||||
mediaUploader.on('select', function() {
|
||||
const attachment = mediaUploader.state().get('selection').first().toJSON();
|
||||
targetField.val(attachment.url);
|
||||
|
||||
const preview = targetField.closest('.roi-form-field-media').find('img');
|
||||
if (preview.length) {
|
||||
preview.attr('src', attachment.url);
|
||||
} else {
|
||||
targetField.closest('.input-group').after('<div class="mt-2"><img src="' + attachment.url + '" alt="Preview" style="max-width: 100px; height: auto;"></div>');
|
||||
}
|
||||
});
|
||||
|
||||
mediaUploader.open();
|
||||
});
|
||||
|
||||
// Color picker sync
|
||||
$('.form-control-color').on('change', function() {
|
||||
$(this).next('input[type="text"]').val($(this).val());
|
||||
});
|
||||
|
||||
// Auto-update preview
|
||||
$('.roi-form-field input, .roi-form-field select, .roi-form-field textarea').on('change keyup', function() {
|
||||
updatePreview();
|
||||
});
|
||||
|
||||
function updatePreview() {
|
||||
// Aquí iría la lógica para actualizar la vista previa en tiempo real
|
||||
console.log('Preview updated');
|
||||
}
|
||||
});
|
||||
})(jQuery);
|
||||
</script>
|
||||
SCRIPT;
|
||||
}
|
||||
|
||||
public function supports(string $componentType): bool
|
||||
{
|
||||
return $componentType === 'top-notification-bar';
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user