feat: LOPDP Ecuador — router, permalinks, política privacidad, banner, nav, index temas

This commit is contained in:
2026-06-05 15:06:52 -05:00
parent 5d575db5e3
commit 90fee1947f
16 changed files with 180 additions and 108 deletions
+19
View File
@@ -6,6 +6,25 @@ Formato: `X.Y.Z` según `VERSIONADO.md`
## [1.1.0] — 2026-06-05
### feat: LOPDP Ecuador completo
- **router.php** — PHP built-in server ahora soporta permalinks (`/%postname%/`).
- **start-wordpress.sh** — actualizado para usar `router.php`.
- **Permalinks** configurados a `/%postname%/` en BD dev y producción.
- **Navegación** en los 4 temas usa `home_url()` base cuando no es front page, para que los links ancla funcionen desde páginas internas.
- **index.php** reescrito en los 4 temas con estilo propio del tema, botón "Volver al CV" y tipografía coherente.
- **Página Política de Privacidad** (`/politica-de-privacidad/`) creada en BD con contenido LOPDP completo (Art. 26 LOPDP Ecuador, base legal interés legítimo, datos anónimos).
- **Banner LOPDP** en footer: fondo navy `#1B3A5C`, botón rojo `#c0392b`, `display:flex` sin `!important`, JS con `localStorage`, responsive 600px. Sin los bugs de `removeAttribute('hidden')` ni `flex!important`.
- **Widget dashboard** "CV Patricia Flores — Estadísticas de visitas" con 4 temas + total.
- **Link de privacidad** en footer de los 4 temas usando `home_url()`.
### fix: cédula eliminada del sitio público (incluido en esta versión)
- Quitada la cédula (1103591648) de `pflores_get_cv_data()`, fallback y todos los `inc/cv-data.php`.
- Limpiada de BD dev y producción.
---
## [1.1.0] — 2026-06-05 (revisado)
### fix: eliminar cédula del sitio público
- Quitada la cédula (1103591648) de `pflores_get_cv_data()`, `pflores_cv_fallback_data()` y todos los `inc/cv-data.php` de los 4 temas.
- Limpiados los campos `'id'` y `'C.I.'` de las opciones `pflores_perfil` en BD dev (`.20`) y producción (`.32`).
+7
View File
@@ -0,0 +1,7 @@
<?php
$path = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
$file = __DIR__ . '/wordpress' . $path;
if ($path !== '/' && file_exists($file) && !is_dir($file)) {
return false;
}
require_once __DIR__ . '/wordpress/index.php';
+1 -1
View File
@@ -39,4 +39,4 @@ else
fi
echo "Iniciando WordPress en http://${WP_HOST}:${WP_PORT}"
exec php -S "${WP_HOST}:${WP_PORT}" -t "$WP_DIR"
exec php -S "${WP_HOST}:${WP_PORT}" -t "$WP_DIR" "$ROOT_DIR/backend/router.php"
@@ -330,14 +330,10 @@ function pflores_get_cv_data(): array
3. LOPDP — Política de privacidad, banner y widget
────────────────────────────────────────────── */
// A) Crear página de Política de Privacidad (una sola vez)
// A) Crear / reparar página de Política de Privacidad
add_action('admin_init', 'pflores_create_privacy_page');
function pflores_create_privacy_page(): void
{
if (get_option('pflores_privacy_page_created')) {
return;
}
$content = '<h2>Responsable del tratamiento</h2>
<p>Patricia Soledad Flores Sánchez · <a href="mailto:patricflorsanchez@gmail.com">patricflorsanchez@gmail.com</a> · pflores.linkedcorp.net</p>
@@ -359,6 +355,28 @@ function pflores_create_privacy_page(): void
<h2>Última actualización</h2>
<p>Junio de 2026.</p>';
// Si ya existe, reparar si tiene contenido genérico de WordPress
$existing = get_page_by_path('politica-de-privacidad');
if ($existing) {
update_option('pflores_privacy_page_created', $existing->ID);
if (str_contains((string) $existing->post_content, 'Qui') ||
str_contains((string) $existing->post_content, 'sample') ||
trim($existing->post_content) === '') {
wp_update_post([
'ID' => $existing->ID,
'post_content' => $content,
'post_status' => 'publish',
'post_name' => 'politica-de-privacidad',
]);
}
return;
}
// Si no existe, crearla
if (get_option('pflores_privacy_page_created')) {
return;
}
$id = wp_insert_post([
'post_title' => 'Política de Privacidad',
'post_name' => 'politica-de-privacidad',
@@ -369,7 +387,14 @@ function pflores_create_privacy_page(): void
]);
if ($id && ! is_wp_error($id)) {
update_option('pflores_privacy_page_created', 1);
update_option('pflores_privacy_page_created', $id);
// Asegurar permalinks bonitos
global $wpdb;
$wpdb->update(
$wpdb->options,
['option_value' => '/%postname%/'],
['option_name' => 'permalink_structure']
);
}
}
@@ -380,6 +405,7 @@ function pflores_lopd_banner(): void
if (is_admin()) {
return;
}
$privacy_url = esc_url(home_url('/politica-de-privacidad/'));
?>
<style>
#pflores-lopd-banner {
@@ -415,21 +441,18 @@ function pflores_lopd_banner(): void
<strong>Ley Orgánica de Protección de Datos Personales del Ecuador (LOPDP)</strong>.
</p>
<div class="pflores-lopd-actions">
<a href="/politica-de-privacidad/" class="pflores-lopd-link">Política de privacidad</a>
<a href="<?php echo $privacy_url; ?>" class="pflores-lopd-link">Política de privacidad</a>
<button id="pflores-lopd-accept" type="button">Aceptar</button>
</div>
</div>
<script>
(function(){
var banner = document.getElementById('pflores-lopd-banner');
if (!banner) return;
if (!localStorage.getItem('pflores_lopd_ok')) {
banner.style.display = 'flex';
}
document.getElementById('pflores-lopd-accept')
.addEventListener('click', function(){
var b = document.getElementById('pflores-lopd-banner');
if (!b) return;
if (!localStorage.getItem('pflores_lopd_ok')) b.style.display = 'flex';
document.getElementById('pflores-lopd-accept').addEventListener('click', function(){
localStorage.setItem('pflores_lopd_ok', '1');
banner.style.display = 'none';
b.style.display = 'none';
});
})();
</script>
@@ -141,7 +141,7 @@ $skills = array_filter($skillsFlat);
</div>
<p style="font-size:.65rem;color:rgba(255,255,255,.25);text-align:center;margin-top:auto;padding-top:.75rem">
<a href="/politica-de-privacidad/" style="opacity:.6;text-decoration:underline;color:inherit">Política de Privacidad · LOPDP</a>
<a href="<?php echo esc_url(home_url('/politica-de-privacidad/')); ?>" style="opacity:.6;text-decoration:underline;color:inherit">Política de Privacidad · LOPDP</a>
</p>
</aside><!-- /.ax-sidebar -->
@@ -73,13 +73,14 @@ add_filter('body_class', 'apex_body_classes');
// ---------------------------------------------------------------------------
function apex_render_nav(): string
{
$base = is_front_page() ? '' : home_url('/');
$items = [
'#sobre-mi' => __('Sobre mí', 'apex'),
'#impacto' => __('Impacto', 'apex'),
'#experiencia' => __('Experiencia', 'apex'),
'#formacion' => __('Formación', 'apex'),
'#certificaciones' => __('Certificaciones', 'apex'),
'#contacto' => __('Contacto', 'apex'),
$base . '#sobre-mi' => __('Sobre mí', 'apex'),
$base . '#impacto' => __('Impacto', 'apex'),
$base . '#experiencia' => __('Experiencia', 'apex'),
$base . '#formacion' => __('Formación', 'apex'),
$base . '#certificaciones' => __('Certificaciones', 'apex'),
$base . '#contacto' => __('Contacto', 'apex'),
];
$html = '<ul class="ax-nav__list">';
@@ -1,24 +1,28 @@
<?php
declare(strict_types=1);
// Fallback template — the theme uses front-page.php for all CV content.
get_header();
?>
<div class="ax-main">
<div class="ax-content-wrap">
<?php if (have_posts()) : ?>
<?php while (have_posts()) : the_post(); ?>
<article id="post-<?php the_ID(); ?>" <?php post_class(); ?>>
<aside class="ax-sidebar" id="ax-sidebar"></aside>
<main class="ax-main">
<div class="ax-content-wrap">
<style>
.ax-page-content h1{font-size:1.6rem;font-weight:700;color:var(--ax-text);margin-bottom:1.5rem;padding-bottom:.75rem;border-bottom:2px solid var(--ax-cyan)}
.ax-page-content h2,.ax-page-content h3{font-weight:600;color:var(--ax-text);margin:1.5rem 0 .5rem}
.ax-page-content h2{font-size:1rem}
.ax-page-content p{font-size:.9rem;color:var(--ax-muted);line-height:1.75;margin-bottom:.75rem}
.ax-page-content a{color:var(--ax-cyan)}
.ax-page-content a:hover{opacity:.75}
.ax-back{display:inline-flex;align-items:center;gap:.4rem;margin-bottom:2rem;font-size:.78rem;font-weight:700;letter-spacing:.06em;text-transform:uppercase;color:var(--ax-cyan);text-decoration:none}
.ax-back:hover{opacity:.75}
.ax-back::before{content:'←'}
</style>
<div class="ax-section ax-page-content">
<a class="ax-back" href="<?php echo esc_url(home_url('/')); ?>">Volver al CV</a>
<?php if (have_posts()) : while (have_posts()) : the_post(); ?>
<h1><?php the_title(); ?></h1>
<div class="entry-content">
<?php the_content(); ?>
</div>
</article>
<?php endwhile; ?>
<?php else : ?>
<p><?php esc_html_e('No se encontró contenido.', 'apex'); ?></p>
<?php endif; ?>
</div>
<?php endwhile; endif; ?>
</div>
</div>
</main>
<?php get_footer(); ?>
+1 -1
View File
@@ -3,7 +3,7 @@
<div class="site-footer__inner">
<p><?php echo esc_html(get_bloginfo('name')); ?> · <?php echo esc_html(date_i18n('Y')); ?>
&nbsp;·&nbsp;
<a href="/politica-de-privacidad/" style="opacity:.6;text-decoration:underline;font-size:.75rem;color:inherit">Política de Privacidad · LOPDP</a></p>
<a href="<?php echo esc_url(home_url('/politica-de-privacidad/')); ?>" style="opacity:.6;text-decoration:underline;font-size:.75rem;color:inherit">Política de Privacidad · LOPDP</a></p>
</div>
</footer>
<?php wp_footer(); ?>
+7 -6
View File
@@ -92,13 +92,14 @@ function cvolio_render_primary_menu(): string
]));
}
$base = is_front_page() ? '' : home_url('/');
$items = [
'#resumen' => __('Resumen', 'cvolio'),
'#impacto' => __('Impacto', 'cvolio'),
'#experiencia' => __('Experiencia', 'cvolio'),
'#formacion' => __('Formación', 'cvolio'),
'#certificaciones' => __('Certificaciones', 'cvolio'),
'#contacto' => __('Contacto', 'cvolio'),
$base . '#resumen' => __('Resumen', 'cvolio'),
$base . '#impacto' => __('Impacto', 'cvolio'),
$base . '#experiencia' => __('Experiencia', 'cvolio'),
$base . '#formacion' => __('Formación', 'cvolio'),
$base . '#certificaciones' => __('Certificaciones', 'cvolio'),
$base . '#contacto' => __('Contacto', 'cvolio'),
];
$html = '<ul class="site-nav__menu">';
+22 -19
View File
@@ -1,23 +1,26 @@
<?php get_header(); ?>
<section class="content-wrap">
<div class="content-card">
<?php if (have_posts()) : ?>
<?php while (have_posts()) : the_post(); ?>
<article <?php post_class(); ?>>
<h1 class="entry-title"><?php the_title(); ?></h1>
<div class="entry-content">
<?php
declare(strict_types=1);
get_header();
?>
<style>
.cv-page-wrap{max-width:780px;margin:3rem auto;padding:0 2rem 4rem}
.cv-page-wrap h1{font-family:'Manrope',sans-serif;font-size:2rem;font-weight:700;color:#d4a574;margin-bottom:1.5rem;padding-bottom:.75rem;border-bottom:2px solid rgba(212,165,116,.4)}
.cv-page-wrap h2,.cv-page-wrap h3{font-family:'Manrope',sans-serif;font-weight:700;color:#d4a574;margin:1.75rem 0 .5rem}
.cv-page-wrap h2{font-size:1.1rem}
.cv-page-wrap p{font-size:.93rem;color:#c8c8d0;line-height:1.75;margin-bottom:.75rem}
.cv-page-wrap a{color:#d4a574}
.cv-page-wrap a:hover{color:#fff}
.cv-back{display:inline-flex;align-items:center;gap:.4rem;margin-bottom:2rem;font-family:'Manrope',sans-serif;font-size:.8rem;font-weight:700;letter-spacing:.06em;text-transform:uppercase;color:#d4a574;text-decoration:none}
.cv-back:hover{color:#fff}
.cv-back::before{content:'←'}
</style>
<section class="content-wrap" style="min-height:60vh">
<div class="cv-page-wrap">
<a class="cv-back" href="<?php echo esc_url(home_url('/')); ?>">Volver al CV</a>
<?php if (have_posts()) : while (have_posts()) : the_post(); ?>
<h1><?php the_title(); ?></h1>
<?php the_content(); ?>
</div>
</article>
<?php endwhile; ?>
<?php else : ?>
<article class="content-empty">
<h1><?php esc_html_e('Empieza a construir tu perfil', 'cvolio'); ?></h1>
<p><?php esc_html_e('Crea una pagina y usa este tema como base para tu hoja de vida.', 'cvolio'); ?></p>
</article>
<?php endif; ?>
<?php endwhile; endif; ?>
</div>
</section>
<?php get_footer(); ?>
+1 -1
View File
@@ -9,7 +9,7 @@ declare(strict_types=1);
<p class="lm-footer__copy">
<?php echo esc_html(get_bloginfo('name')); ?> &middot; <?php echo esc_html(date_i18n('Y')); ?>
&nbsp;&middot;&nbsp;
<a href="/politica-de-privacidad/" style="opacity:.6;text-decoration:underline;font-size:.75rem;color:inherit">Política de Privacidad · LOPDP</a>
<a href="<?php echo esc_url(home_url('/politica-de-privacidad/')); ?>" style="opacity:.6;text-decoration:underline;font-size:.75rem;color:inherit">Política de Privacidad · LOPDP</a>
</p>
<a class="lm-footer__top" href="#top" aria-label="<?php esc_attr_e('Volver al inicio', 'lumina'); ?>">&#8593;</a>
</div>
+7 -6
View File
@@ -91,13 +91,14 @@ function lumina_render_primary_menu(): string
]));
}
$base = is_front_page() ? '' : home_url('/');
$items = [
'#resumen' => __('Perfil', 'lumina'),
'#impacto' => __('Impacto', 'lumina'),
'#experiencia' => __('Experiencia', 'lumina'),
'#formacion' => __('Formación', 'lumina'),
'#certificaciones' => __('Certificaciones', 'lumina'),
'#contacto' => __('Contacto', 'lumina'),
$base . '#resumen' => __('Perfil', 'lumina'),
$base . '#impacto' => __('Impacto', 'lumina'),
$base . '#experiencia' => __('Experiencia', 'lumina'),
$base . '#formacion' => __('Formación', 'lumina'),
$base . '#certificaciones' => __('Certificaciones', 'lumina'),
$base . '#contacto' => __('Contacto', 'lumina'),
];
$html = '<ul class="lm-nav__list">';
+18 -20
View File
@@ -1,28 +1,26 @@
<?php
declare(strict_types=1);
get_header();
?>
<section class="lm-wrap">
<div class="lm-card">
<?php if (have_posts()) : ?>
<?php while (have_posts()) : the_post(); ?>
<article <?php post_class('lm-entry'); ?>>
<h1 class="lm-entry__title"><?php the_title(); ?></h1>
<div class="lm-entry__body">
<style>
.lm-page-wrap{max-width:780px;margin:3rem auto;padding:0 2rem 4rem}
.lm-page-wrap h1{font-family:'Plus Jakarta Sans',sans-serif;font-size:2rem;font-weight:700;color:#0f766e;margin-bottom:1.5rem;padding-bottom:.75rem;border-bottom:3px solid #f59e0b}
.lm-page-wrap h2,.lm-page-wrap h3{font-family:'Plus Jakarta Sans',sans-serif;font-weight:700;color:#0f766e;margin:1.75rem 0 .5rem}
.lm-page-wrap h2{font-size:1.15rem}
.lm-page-wrap p{font-size:.95rem;color:#334155;line-height:1.75;margin-bottom:.75rem}
.lm-page-wrap a{color:#0d9488}
.lm-page-wrap a:hover{color:#f59e0b}
.lm-back{display:inline-flex;align-items:center;gap:.4rem;margin-bottom:2rem;font-family:'Plus Jakarta Sans',sans-serif;font-size:.82rem;font-weight:700;letter-spacing:.06em;text-transform:uppercase;color:#0d9488;text-decoration:none}
.lm-back:hover{color:#f59e0b}
.lm-back::before{content:'←'}
</style>
<section style="background:#f8fafc;min-height:60vh">
<div class="lm-page-wrap">
<a class="lm-back" href="<?php echo esc_url(home_url('/')); ?>">Volver al CV</a>
<?php if (have_posts()) : while (have_posts()) : the_post(); ?>
<h1><?php the_title(); ?></h1>
<?php the_content(); ?>
</div>
</article>
<?php endwhile; ?>
<?php else : ?>
<article class="lm-entry lm-entry--empty">
<h1><?php esc_html_e('Empieza a construir tu perfil', 'lumina'); ?></h1>
<p><?php esc_html_e('Crea una página de inicio y usa Lumina CV como tema activo.', 'lumina'); ?></p>
</article>
<?php endif; ?>
<?php endwhile; endif; ?>
</div>
</section>
<?php get_footer(); ?>
+1 -1
View File
@@ -375,7 +375,7 @@ $publicUrl = 'https://pflres.linkedcorp.net';
<div class="pn-footer-bar">
<p><?php echo esc_html(get_bloginfo('name')); ?> &copy; <?php echo esc_html(date_i18n('Y')); ?> &mdash; <?php esc_html_e('Todos los derechos reservados', 'persona'); ?></p>
<a href="/politica-de-privacidad/" style="opacity:.6;text-decoration:underline;font-size:.75rem;color:inherit">Política de Privacidad · LOPDP</a>
<a href="<?php echo esc_url(home_url('/politica-de-privacidad/')); ?>" style="opacity:.6;text-decoration:underline;font-size:.75rem;color:inherit">Política de Privacidad · LOPDP</a>
</div>
</div>
</section>
+8 -7
View File
@@ -50,14 +50,15 @@ function persona_render_nav(): string {
'fallback_cb' => false, 'menu_class' => 'pn-nav__list', 'echo' => false,
]));
}
$base = is_front_page() ? '' : home_url('/');
$items = [
'#sobre-mi' => 'Sobre mí',
'#impacto' => 'Impacto',
'#experiencia' => 'Experiencia',
'#formacion' => 'Formación',
'#competencias' => 'Competencias',
'#certificaciones' => 'Certificaciones',
'#contacto' => 'Contacto',
$base . '#sobre-mi' => 'Sobre mí',
$base . '#impacto' => 'Impacto',
$base . '#experiencia' => 'Experiencia',
$base . '#formacion' => 'Formación',
$base . '#competencias' => 'Competencias',
$base . '#certificaciones' => 'Certificaciones',
$base . '#contacto' => 'Contacto',
];
$html = '<ul class="pn-nav__list">';
foreach ($items as $href => $label) {
+16 -2
View File
@@ -2,10 +2,24 @@
declare(strict_types=1);
get_header();
?>
<style>
.pn-page-wrap{max-width:780px;margin:3rem auto;padding:0 2rem 4rem}
.pn-page-wrap h1{font-family:'Raleway',sans-serif;font-size:2rem;font-weight:700;color:#1B3A5C;margin-bottom:1.5rem;padding-bottom:.75rem;border-bottom:3px solid #c0392b}
.pn-page-wrap h2,.pn-page-wrap h3{font-family:'Raleway',sans-serif;font-weight:700;color:#1B3A5C;margin:1.75rem 0 .5rem}
.pn-page-wrap h2{font-size:1.15rem}
.pn-page-wrap p{font-size:.95rem;color:#2d3436;line-height:1.75;margin-bottom:.75rem}
.pn-page-wrap a{color:#c0392b}
.pn-page-wrap a:hover{color:#1B3A5C}
.pn-back{display:inline-flex;align-items:center;gap:.4rem;margin-bottom:2rem;font-family:'Raleway',sans-serif;font-size:.82rem;font-weight:700;letter-spacing:.06em;text-transform:uppercase;color:#c0392b;text-decoration:none}
.pn-back:hover{color:#1B3A5C}
.pn-back::before{content:'←'}
</style>
<section class="pn-section pn-section--white">
<div class="pn-container">
<div class="pn-page-wrap">
<a class="pn-back" href="<?php echo esc_url(home_url('/')); ?>">Volver al CV</a>
<?php if (have_posts()) : while (have_posts()) : the_post(); ?>
<article <?php post_class(); ?>><h1><?php the_title(); ?></h1><?php the_content(); ?></article>
<h1><?php the_title(); ?></h1>
<?php the_content(); ?>
<?php endwhile; endif; ?>
</div>
</section>