<?php /** * La Frontera IA Child Theme — Functions & Shortcodes v2.0 * Diseño completo con sistema de componentes reutilizables */ // Enqueue child theme styles after parent add_action('wp_enqueue_scripts', 'lfi_enqueue_styles'); // ============================================ // FIXES: Ocultar prefijo "Privado:" de posts privados // ============================================ add_filter('private_title_format', '__return_empty_string'); add_filter('protected_title_format', '__return_empty_string'); // ============================================ // LAZY-LOADING: Asegurar lazy loading en imágenes // ============================================ add_filter('wp_get_attachment_image_attributes', 'lfi_lazy_load_attachments', 10, 3); function lfi_lazy_load_attachments($attr, $attachment, $size) { $attr['loading'] = 'lazy'; $attr['decoding'] = 'async'; return $attr; } // Add lazy loading to img tags in post content (safety net) add_filter('the_content', 'lfi_add_lazy_to_images', 100); function lfi_add_lazy_to_images($content) { // Only add if not already present $content = preg_replace('/<img(?!.*loading=)/i', '<img loading="lazy" decoding="async"', $content); return $content; } // ============================================ // ANALYTICS & TRACKING // ============================================ // Google Search Console verification add_action('wp_head', 'lfi_add_gsc_meta'); function lfi_add_gsc_meta() { // Replace with your GSC verification code echo '<meta name="google-site-verification" content="PON_AQUI_TU_CODIGO_GSC" />' . "\n"; } // Add Google Analytics / Plausible / Umami snippet placeholder add_action('wp_head', 'lfi_add_tracking_head', 99); function lfi_add_tracking_head() { // Plausible, Umami or GA4 snippet goes here } // ============================================ // CSS OVERRIDES — Inline CSS prioritario (gana a inline de plugins) // ============================================ add_action('wp_head', 'lfi_add_css_overrides', 999); function lfi_add_css_overrides() { echo '<style id="lfi-css-overrides">' . "\n"; echo '/* Neutralizar BoldTimber top nav */' . "\n"; echo '.bt-topnav { display: none !important; }' . "\n"; echo 'body > .wp-site-blocks > header.wp-block-template-part { display: block !important; }' . "\n"; echo '/* Ocultar hamburguesa en desktop (>768px) */' . "\n"; echo '@media (min-width: 769px) {' . "\n"; echo ' .wp-block-navigation__responsive-container-open { display: none !important; }' . "\n"; echo '}' . "\n"; echo '/* Prevenir overflow horizontal */' . "\n"; echo 'html, body { overflow-x: hidden !important; }' . "\n"; echo '/* Ajuste post layout BT en móvil */' . "\n"; echo '@media (max-width: 768px) {' . "\n"; echo ' .bt-post-layout { display: block !important; }' . "\n"; echo ' .bt-sidebar { display: none !important; }' . "\n"; echo ' .entry-content, .bt-post-layout > * { max-width: 100% !important; overflow-wrap: break-word !important; }' . "\n"; echo '}' . "\n"; echo '/* Restaurar body font */' . "\n"; echo 'body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif !important; }' . "\n"; echo '/* === LAYOUT OVERRIDES: forzar padding mínimo en contenido === */' . "\n"; echo 'body.single .wp-block-post-content {' . "\n"; echo ' padding-left: 0 !important;' . "\n"; echo ' padding-right: 0 !important;' . "\n"; echo ' max-width: 720px !important;' . "\n"; echo ' margin-left: auto !important;' . "\n"; echo ' margin-right: auto !important;' . "\n"; echo '}' . "\n"; echo 'body.single main {' . "\n"; echo ' padding: 24px 0 !important;' . "\n"; echo ' margin-top: 0 !important;' . "\n"; echo ' max-width: 100% !important;' . "\n"; echo '}' . "\n"; echo '.wp-site-blocks {' . "\n"; echo ' max-width: 100% !important;' . "\n"; echo '}' . "\n"; echo '/* Móvil: nada de padding lateral en el contenido */' . "\n"; echo '@media (max-width: 768px) {' . "\n"; echo ' body.single .wp-block-post-content {' . "\n"; echo ' padding-left: 0 !important;' . "\n"; echo ' padding-right: 0 !important;' . "\n"; echo ' }' . "\n"; echo ' body.single main {' . "\n"; echo ' padding: 8px 0 !important;' . "\n"; echo ' }' . "\n"; echo ' /* Neutralizar los groups anidados con padding de theme.json */' . "\n"; echo ' body.single .wp-block-group.has-global-padding,' . "\n"; echo ' body.single .is-layout-constrained {' . "\n"; echo ' padding-left: 0 !important;' . "\n"; echo ' padding-right: 0 !important;' . "\n"; echo ' }' . "\n"; echo '}' . "\n"; echo '</style>' . "\n"; } // Click tracking for affiliate links (adds onclick data) add_filter('the_content', 'lfi_track_affiliate_clicks', 50); function lfi_track_affiliate_clicks($content) { // Only run on singular posts if (!is_singular('post')) return $content; // Add data-tracking attributes to affiliate links // Hostinger, Amazon, DigitalOcean, etc. $affiliate_domains = [ 'hostinger.es', 'amazon.es', 'amazon.com', 'digitalocean.com', 'semrush.com', 'vultr.com', 'siteground.com', 'elementor.com', 'convertkit.com', ]; foreach ($affiliate_domains as $domain) { $escaped_domain = preg_quote($domain, '/'); $content = preg_replace( '/<a\s+(.*?href="https?:\/\/[^"]*' . $escaped_domain . '[^"]*"[^>]*)>/i', '<a $1 data-track="affiliate" data-domain="' . $domain . '">', $content ); } return $content; } // ============================================ // STYLES: Cargar CSS del child theme // ============================================ function lfi_enqueue_styles() { $version = filemtime(get_stylesheet_directory() . '/style.css'); wp_enqueue_style('lfi-child-style', get_stylesheet_uri(), ['twentytwentyfive-style'], $version); } // ============================================ // HELPER: Renderizar estrella de rating // ============================================ function lfi_render_stars($rating) { $rating = floatval($rating); $full = floor($rating); $half = ($rating - $full) >= 0.5; $stars = ''; for ($i = 0; $i < $full; $i++) { $stars .= '★'; } if ($half) { $stars .= '★'; // simplificado: media estrella como entera } $empty = 5 - $full - ($half ? 1 : 0); $stars .= str_repeat('☆', max(0, $empty)); return sprintf('<span class="lfi-stars">%s</span>', $stars); } // ============================================ // SHORTCODE: [lfi-box tipo="info|warning|important|tip"]...[/lfi-box] // Uso: [lfi-box tipo="tip"]Contenido[/lfi-box] // ============================================ add_shortcode('lfi-box', 'lfi_box_shortcode'); function lfi_box_shortcode($atts, $content = '') { $tipo = isset($atts['tipo']) ? sanitize_html_class($atts['tipo']) : 'info'; $validos = ['info', 'warning', 'important', 'tip']; if (!in_array($tipo, $validos)) $tipo = 'info'; $content = wp_kses_post($content); return sprintf( '<div class="lfi-box lfi-box-%s">%s</div>', esc_attr($tipo), $content ); } // ============================================ // SHORTCODE: [lfi-cta id="hostinger" variante="principal" texto="Ver oferta →" url=""] // Uso: [lfi-cta id="hostinger"] // Uso: [lfi-cta url="https://..." texto="Probar gratis →"] // ============================================ add_shortcode('lfi-cta', 'lfi_cta_shortcode'); function lfi_cta_shortcode($atts) { $id = isset($atts['id']) ? sanitize_html_class($atts['id']) : ''; $variante = isset($atts['variante']) ? sanitize_html_class($atts['variante']) : 'principal'; $texto = isset($atts['texto']) ? esc_html($atts['texto']) : 'Ver oferta →'; $url = isset($atts['url']) ? esc_url($atts['url']) : ''; $precio = isset($atts['precio']) ? esc_html($atts['precio']) : ''; if (!$id && !$url) return ''; // Mapa de afiliados $afiliados = [ 'hostinger' => ['url' => 'https://www.hostinger.es/visitors/lafronteraia.com', 'nombre' => 'Hostinger'], 'digitalocean' => ['url' => 'https://www.digitalocean.com/?ref=lafronteraia', 'nombre' => 'DigitalOcean'], 'semrush' => ['url' => 'https://www.semrush.com/?ref=lafronteraia', 'nombre' => 'Semrush'], 'vultr' => ['url' => 'https://www.vultr.com/?ref=lafronteraia', 'nombre' => 'Vultr'], 'notion' => ['url' => 'https://www.notion.so/?ref=lafronteraia', 'nombre' => 'Notion'], 'cloudflare' => ['url' => 'https://www.cloudflare.com/?ref=lafronteraia', 'nombre' => 'Cloudflare'], 'openai' => ['url' => 'https://openai.com/?ref=lafronteraia', 'nombre' => 'OpenAI'], 'canva' => ['url' => 'https://www.canva.com/?ref=lafronteraia', 'nombre' => 'Canva'], 'wordpress' => ['url' => 'https://wordpress.com/?ref=lafronteraia', 'nombre' => 'WordPress.com'], 'webflow' => ['url' => 'https://webflow.com/?ref=lafronteraia', 'nombre' => 'Webflow'], ]; $nombre = $id && isset($afiliados[$id]) ? $afiliados[$id]['nombre'] : ucfirst($id); if (!$url && $id && isset($afiliados[$id])) { $url = $afiliados[$id]['url']; } if (!$url) return ''; $btn_class = $variante === 'secundario' ? 'lfi-btn-secondary' : 'lfi-btn-cta'; $price_html = $precio ? sprintf(' <span class="price">%s</span>', $precio) : ''; return sprintf( '<div class="lfi-affiliate-card %s"> <div class="lfi-affiliate-info"> <h4>%s</h4> <a href="%s" class="lfi-btn %s" target="_blank" rel="nofollow sponsored">%s%s</a> </div> </div>', $variante === 'principal' ? 'featured' : '', esc_html($nombre), $url, $btn_class, $texto, $price_html ); } // ============================================ // SHORTCODE: [lfi-table tipo="comparativa"]..HTML..[/lfi-table] // Uso: [lfi-table]Tabla HTML[/lfi-table] // ============================================ add_shortcode('lfi-table', 'lfi_table_shortcode'); function lfi_table_shortcode($atts, $content = '') { $tipo = isset($atts['tipo']) ? sanitize_html_class($atts['tipo']) : 'comparativa'; // No sanitizamos porque es HTML de tabla return sprintf( '<div class="lfi-table-wrapper"><table class="lfi-table lfi-table-%s">%s</table></div>', esc_attr($tipo), $content ); } // ============================================ // SHORTCODE: [lfi-review score="8.5" title="Veredicto"]...texto...[/lfi-review] // Uso: [lfi-review score="9.2"]Texto del veredicto[/lfi-review] // ============================================ add_shortcode('lfi-review', 'lfi_review_shortcode'); function lfi_review_shortcode($atts, $content = '') { $score = isset($atts['score']) ? floatval($atts['score']) : 0; $title = isset($atts['title']) ? esc_html($atts['title']) : 'Veredicto'; $content = wp_kses_post($content); $score_clean = number_format($score, 1); $label = $score >= 9 ? 'Excelente' : ($score >= 7.5 ? 'Muy bueno' : ($score >= 6 ? 'Bueno' : 'Regular')); return sprintf( '<div class="lfi-review"> <div class="lfi-review-score"> <span class="score-number">%s</span> <span class="score-label">%s</span> </div> <div class="lfi-review-content"> <h4>%s</h4> %s </div> </div>', esc_html($score_clean), esc_html($label), esc_html($title), $content ); } // ============================================ // SHORTCODE: [lfi-best-pick title="Nuestra elección"]...texto...[/lfi-best-pick] // Para destacar "el mejor" tipo Wirecutter // ============================================ add_shortcode('lfi-best-pick', 'lfi_best_pick_shortcode'); function lfi_best_pick_shortcode($atts, $content = '') { $title = isset($atts['title']) ? esc_html($atts['title']) : 'Nuestra elección'; $content = wp_kses_post($content); return sprintf( '<div class="lfi-best-pick"> <div class="lfi-best-pick-icon">🏆</div> <div class="lfi-best-pick-content"> <h4>%s</h4> %s </div> </div>', esc_html($title), $content ); } // ============================================ // SHORTCODE: [lfi-pros-cons] // [pros]Ventaja 1||Ventaja 2[/pros] // [cons]Desventaja 1||Desventaja 2[/cons] // [/lfi-pros-cons] // ============================================ add_shortcode('lfi-pros-cons', 'lfi_pros_cons_shortcode'); function lfi_pros_cons_shortcode($atts, $content = '') { // Parse inner shortcodes $content = do_shortcode($content); return $content; // El HTML viene del shortcode interno } // Helper inner shortcodes for pros/cons add_shortcode('pros', 'lfi_pros_inner'); function lfi_pros_inner($atts, $content = '') { $items = explode('||', $content); $lis = ''; foreach ($items as $item) { $item = trim($item); if ($item) $lis .= sprintf('<li>%s</li>', esc_html($item)); } // Re-wrap: check if inside a container return sprintf( '<div class="lfi-pros"><p class="lfi-pros-title">✅ Ventajas</p><ul>%s</ul></div>', $lis ); } add_shortcode('cons', 'lfi_cons_inner'); function lfi_cons_inner($atts, $content = '') { $items = explode('||', $content); $lis = ''; foreach ($items as $item) { $item = trim($item); if ($item) $lis .= sprintf('<li>%s</li>', esc_html($item)); } return sprintf( '<div class="lfi-cons"><p class="lfi-cons-title">❌ Desventajas</p><ul>%s</ul></div>', $lis ); } // ============================================ // SHORTCODE: [lfi-comparativa] ... contenido ... [/lfi-comparativa] // No es un shortcode renderizable directo, se usa el shortcode de tabla + best-pick // ============================================ // ============================================ // SHORTCODE: [lfi-author name="Jefrey" bio="Experto en IA"]Avatar[/lfi-author] // Uso: [lfi-author name="Jefrey" bio="Editor en La Frontera IA"]<img...>[/lfi-author] // ============================================ add_shortcode('lfi-author', 'lfi_author_shortcode'); function lfi_author_shortcode($atts, $content = '') { $name = isset($atts['name']) ? esc_html($atts['name']) : 'Editor'; $bio = isset($atts['bio']) ? esc_html($atts['bio']) : ''; $content = $content; // puede ser HTML de avatar $avatar_html = ''; if ($content) { $avatar_html = sprintf('<div class="lfi-author-avatar" style="background-image:url(\'%s\')"></div>', esc_url(trim($content))); } $bio_html = $bio ? sprintf('<p class="lfi-author-bio">%s</p>', $bio) : ''; return sprintf( '<div class="lfi-author-box"> %s <div class="lfi-author-info"> <h4 class="lfi-author-name">%s</h4> %s </div> </div>', $avatar_html, esc_html($name), $bio_html ); } // ============================================ // SHORTCODE: [lfi-interlink url="https://..." texto="Leer más"]Título del artículo[/lfi-interlink] // Para interlinking contextual inline // ============================================ add_shortcode('lfi-interlink', 'lfi_interlink_shortcode'); function lfi_interlink_shortcode($atts, $content = '') { $url = isset($atts['url']) ? esc_url($atts['url']) : ''; $texto = isset($atts['texto']) ? esc_html($atts['texto']) : 'Leer más →'; $title = $content ? wp_kses_post($content) : ''; if (!$url) return ''; $title_html = $title ? sprintf('<strong>%s</strong> — ', $title) : ''; return sprintf( '<div class="lfi-interlink"> <span class="lfi-interlink-icon">🔗</span> <span>%s<a href="%s" class="lfi-interlink-link">%s</a></span> </div>', $title_html, $url, $texto ); } // ============================================ // SHORTCODE: [lfi-related]...HTML de grid...[/lfi-related] // Envuelve en un grid responsive de cards // ============================================ add_shortcode('lfi-related', 'lfi_related_shortcode'); function lfi_related_shortcode($atts, $content = '') { $title = isset($atts['title']) ? esc_html($atts['title']) : 'Artículos relacionados'; $content = $content; // HTML return sprintf( '<div class="lfi-related-section"><h3>%s</h3><div class="lfi-related-posts">%s</div></div>', $title, $content ); } // ============================================ // SHORTCODE: [lfi-key-takeaway]...contenido...[/lfi-key-takeaway] // Caja destacada al inicio del artículo // ============================================ add_shortcode('lfi-key-takeaway', 'lfi_key_takeaway_shortcode'); function lfi_key_takeaway_shortcode($atts, $content = '') { $content = wp_kses_post($content); return sprintf( '<div class="lfi-key-takeaway"><strong>En resumen:</strong> %s</div>', $content ); } // ============================================ // SHORTCODE: [lfi-toc] ... auto-generado de H2/H3 ... */ // Genera TOC escaneando solo encabezados H2/H3 reales. // Ignora shortcodes, bloques, cajas, CTAs, footer. // ============================================ add_shortcode('lfi-toc', 'lfi_toc_shortcode'); function lfi_toc_shortcode($atts, $content = '') { $title = isset($atts['title']) ? esc_html($atts['title']) : 'Índice de contenidos'; // Auto-generate from [lfi-toc] if no content provided, // but only actual H2/H3 headings, not raw HTML. // The auto-generation is done at render time from global $post->post_content global $post; if (!$content && $post) { $full_content = $post->post_content; // Remove shortcodes from the text we scan $clean = preg_replace('/\[lfi-[^\]]*\]/', '', $full_content); $clean = preg_replace('/\[\/?[a-z_-]+\]/', '', $clean); preg_match_all('/<h2[^>]*>(.*?)<\/h2>|<h3[^>]*>(.*?)<\/h3>/is', $clean, $matches, PREG_SET_ORDER); if (!empty($matches)) { $items = []; foreach ($matches as $m) { $text = strip_tags($m[1] ?: $m[2]); $text = preg_replace('/\[[^\]]*\]/', '', $text); // strip any leftover brackets $text = trim($text); if (!$text) continue; $anchor = sanitize_title($text); $tag = $m[1] ? 'h2' : 'h3'; $indent = ($tag === 'h3') ? ' style="padding-left:16px"' : ''; $items[] = sprintf( '<li%s><a href="#%s">%s</a></li>', $indent, esc_attr($anchor), esc_html($text) ); } if (!empty($items)) { $content = '<ul class="lfi-toc-list">' . implode('\n', $items) . '</ul>'; } } } if (!$content) { return ''; // No headings found, hide TOC } $content = trim($content); return sprintf( '<div class="lfi-toc"> <p class="lfi-toc-title">📑 %s</p> %s </div>', $title, $content ); } // ============================================ // SHORTCODE: [lfi-newsletter] ... mensaje personalizado ... [/lfi-newsletter] // CTA de suscripción inline // ============================================ add_shortcode('lfi-newsletter', 'lfi_newsletter_shortcode'); function lfi_newsletter_shortcode($atts, $content = '') { $title = isset($atts['title']) ? esc_html($atts['title']) : '¿Te ha gustado este artículo?'; $cta_text = isset($atts['cta']) ? esc_html($atts['cta']) : 'Suscribirme'; $content = $content ? wp_kses_post($content) : 'Recibe contenido como este directamente en tu correo. Sin spam, sin relleno.'; return sprintf( '<div class="lfi-newsletter-inline"> <h3>%s</h3> <p>%s</p> <div class="lfi-newsletter-form"> <input type="email" placeholder="tu@email.com" aria-label="Tu email"> <a href="#" class="lfi-btn lfi-btn-primary">%s</a> </div> </div>', esc_html($title), $content, esc_html($cta_text) ); } // ============================================ // SHORTCODE: [lfi-steps]...contenido con divs .lfi-step...[/lfi-steps] // Envuelve pasos de tutorial // ============================================ add_shortcode('lfi-steps', 'lfi_steps_shortcode'); function lfi_steps_shortcode($atts, $content = '') { $content = do_shortcode($content); return sprintf('<div class="lfi-steps-wrapper">%s</div>', $content); } // ============================================ // SHORTCODE: [lfi-step number="1"]...contenido...[/lfi-step] // ============================================ add_shortcode('lfi-step', 'lfi_step_shortcode'); function lfi_step_shortcode($atts, $content = '') { $number = isset($atts['number']) ? intval($atts['number']) : ''; $title = isset($atts['title']) ? esc_html($atts['title']) : ''; $content = wp_kses_post($content); $num_html = $number ? sprintf('<div class="lfi-step-number">Paso %d</div>', $number) : ''; $title_html = $title ? sprintf('<strong>%s</strong><br>', $title) : ''; return sprintf( '<div class="lfi-step">%s%s%s</div>', $num_html, $title_html, $content ); } // ============================================ // SHORTCODE: [lfi-code]...código...[/lfi-code] // ============================================ add_shortcode('lfi-code', 'lfi_code_shortcode'); function lfi_code_shortcode($atts, $content = '') { $lang = isset($atts['lang']) ? esc_attr($atts['lang']) : ''; $content = htmlspecialchars($content, ENT_NOQUOTES, 'UTF-8'); $lang_html = $lang ? sprintf(' data-lang="%s"', $lang) : ''; return sprintf( '<pre%s><code>%s</code></pre>', $lang_html, $content ); } // ============================================ // CATEGORY BODY CLASS // ============================================ add_filter('body_class', 'lfi_category_body_class'); function lfi_category_body_class($classes) { if (is_single()) { $categories = get_the_category(); foreach ($categories as $cat) { $classes[] = 'lfi-category-' . $cat->slug; } } return $classes; } // ============================================ // META BOX OUTPUT ABOVE CONTENT // ============================================ add_action('wp', 'lfi_meta_output'); function lfi_meta_output() { if (is_single()) { add_filter('the_content', 'lfi_prepend_meta', 15); } } function lfi_prepend_meta($content) { if (!is_single() || !in_the_loop()) return $content; $categories = get_the_category(); $cat_names = array_map(function($c) { return $c->name; }, $categories); $cat_slugs = array_map(function($c) { return $c->slug; }, $categories); $cat_str = implode(', ', $cat_names); $cat_slug = $cat_slugs[0] ?? ''; // Map category to badge class $badge_map = [ 'comparativas' => ['class' => 'lfi-badge-comparativa', 'label' => 'Comparativa'], 'noticias-ia' => ['class' => 'lfi-badge-noticia', 'label' => 'Noticia'], 'tutoriales' => ['class' => 'lfi-badge-guia', 'label' => 'Guía'], 'herramientas-ia' => ['class' => 'lfi-badge-herramienta', 'label' => 'Herramienta'], 'glosario' => ['class' => 'lfi-badge-glosario', 'label' => 'Glosario'], 'recursos' => ['class' => 'lfi-badge-glosario', 'label' => 'Recurso'], ]; $badge_info = $badge_map[$cat_slug] ?? ['class' => 'lfi-badge-glosario', 'label' => 'Artículo']; $reading_time = get_post_meta(get_the_ID(), '_lfi_reading_time', true); $time_html = $reading_time ? sprintf('<span class="lfi-meta-reading">%s min de lectura</span>', esc_html($reading_time)) : ''; // Show only the primary category badge, not all categories concatenated $primary_badge_label = $badge_info['label']; // Get primary category name for the badge $primary_cat_name = $categories ? $categories[0]->name : ''; $meta = sprintf( '<div class="lfi-meta"> <span class="lfi-meta-category"><span class="lfi-badge %s">%s</span></span> <span class="lfi-meta-updated">✓ %s</span> %s </div>', esc_attr($badge_info['class']), esc_html($primary_badge_label), get_the_modified_date('d/m/Y'), $time_html ); // Extract key-takeaway and TOC from RENDERED content $key_takeaway = ''; $toc = ''; // Match TOC block including nested divs/p if (preg_match('/<div\s+class="lfi-toc">(.*?<\/div>)/s', $content, $toc_match)) { $toc = $toc_match[0]; $content = str_replace($toc_match[0], '', $content); } // Match key takeaway block if (preg_match('/<div\s+class="lfi-key-takeaway">.*?<\/div>/s', $content, $kt_match)) { $key_takeaway = $kt_match[0]; $content = str_replace($kt_match[0], '', $content); } // Render order: meta -> key-takeaway -> toc -> rest $output = $meta; if ($key_takeaway) { $output .= $key_takeaway; } if ($toc) { $output .= $toc; } $output .= $content; return $output; } // ============================================ // PROTECCIÓN: Neutralizar shortcodes no registrados // Si el cron usa un shortcode que no existe, lo reemplaza // por [lfi-box tipo="info"] para evitar HTML crudo visible // ============================================ add_filter('the_content', 'lfi_neutralize_unregistered_shortcodes', 1); function lfi_neutralize_unregistered_shortcodes($content) { // Only process LFI shortcodes that are NOT registered global $shortcode_tags; $lfi_shortcodes = [ 'lfi-box', 'lfi-cta', 'lfi-pros-cons', 'lfi-table', 'lfi-toc', 'lfi-review', 'lfi-bestpick', 'lfi-best-pick', 'lfi-author-box', 'lfi-interlink', 'lfi-newsletter', 'lfi-key-takeaway', 'lfi-steps', 'lfi-step', 'lfi-code', 'lfi-metrics', 'lfi-related', 'lfi-key-takeaway' ]; foreach ($lfi_shortcodes as $sc) { if (!isset($shortcode_tags[$sc])) { // This shortcode is NOT registered // Replace [lfi-xxx]...[/lfi-xxx] with <div class="lfi-box lfi-box-info">...</div> $content = preg_replace( '/\[' . preg_quote($sc, '/') . '(\s+[^\]]*)?\](.*?)\[\/' . preg_quote($sc, '/') . '\]/s', '<div class="lfi-box lfi-box-info">$2</div>', $content ); } } return $content; } // ============================================ // GUTENBERG BLOCK PATTERNS // ============================================ // Register pattern category add_action('init', 'lfi_register_pattern_category'); function lfi_register_pattern_category() { register_block_pattern_category('lfi', [ 'label' => __('La Frontera IA', 'lfi-child'), ]); } add_action('init', 'lfi_register_patterns'); function lfi_register_patterns() { // CTA Afiliado register_block_pattern('lfi/cta-afiliado', [ 'title' => __('CTA Afiliado', 'lfi-child'), 'description' => __('Bloque de llamada a la acción para afiliados', 'lfi-child'), 'categories' => ['lfi'], 'content' => '<!-- wp:shortcode -->[lfi-cta id="hostinger" variante="principal"]<!-- /wp:shortcode -->', ]); // Caja informativa register_block_pattern('lfi/caja-informativa', [ 'title' => __('Caja Informativa', 'lfi-child'), 'description' => __('Caja de información, advertencia o tip', 'lfi-child'), 'categories' => ['lfi'], 'content' => '<!-- wp:shortcode -->[lfi-box tipo="tip"]Escribe aquí tu contenido...[/lfi-box]<!-- /wp:shortcode -->', ]); // Tabla comparativa register_block_pattern('lfi/tabla-comparativa', [ 'title' => __('Tabla Comparativa', 'lfi-child'), 'description' => __('Tabla comparativa responsive con estilo LFI', 'lfi-child'), 'categories' => ['lfi'], 'content' => '<!-- wp:shortcode -->[lfi-table tipo="comparativa"]<thead><tr><th>Característica</th><th>Producto A</th><th>Producto B</th></tr></thead><tbody><tr><td>Precio</td><td>9.99€</td><td>14.99€</td></tr><tr><td>Valoración</td><td>4.8/5</td><td>4.2/5</td></tr></tbody>[/lfi-table]<!-- /wp:shortcode -->', ]); // Pros y contras register_block_pattern('lfi/pros-cons', [ 'title' => __('Pros y Contras', 'lfi-child'), 'description' => __('Dos columnas de ventajas y desventajas', 'lfi-child'), 'categories' => ['lfi'], 'content' => '<!-- wp:shortcode -->[lfi-pros-cons][pros]Rápido||Barato||Fácil de usar[/pros][cons]Caro||Limitado[/cons][/lfi-pros-cons]<!-- /wp:shortcode -->', ]); // Review/Veredicto register_block_pattern('lfi/review', [ 'title' => __('Review con Puntuación', 'lfi-child'), 'description' => __('Bloque de review con puntuación y veredicto', 'lfi-child'), 'categories' => ['lfi'], 'content' => '<!-- wp:shortcode -->[lfi-review score="8.5" title="Veredicto"]<p>Texto del veredicto aquí...</p>[/lfi-review]<!-- /wp:shortcode -->', ]); // Best Pick register_block_pattern('lfi/best-pick', [ 'title' => __('Best Pick (Nuestra elección)', 'lfi-child'), 'description' => __('Destaca una opción como la mejor recomendación', 'lfi-child'), 'categories' => ['lfi'], 'content' => '<!-- wp:shortcode -->[lfi-best-pick title="Nuestra elección"]<p>Texto explicando por qué esta es la mejor opción...</p>[/lfi-best-pick]<!-- /wp:shortcode -->', ]); // Newsletter inline register_block_pattern('lfi/newsletter', [ 'title' => __('Newsletter Inline', 'lfi-child'), 'description' => __('Formulario de suscripción inline en artículo', 'lfi-child'), 'categories' => ['lfi'], 'content' => '<!-- wp:shortcode -->[lfi-newsletter title="¿Te ha gustado?"]Recibe contenido como este directamente en tu correo.[/lfi-newsletter]<!-- /wp:shortcode -->', ]); // Interlink register_block_pattern('lfi/interlink', [ 'title' => __('Enlace Relacionado (Interlink)', 'lfi-child'), 'description' => __('Enlace contextual a otro artículo', 'lfi-child'), 'categories' => ['lfi'], 'content' => '<!-- wp:shortcode -->[lfi-interlink url="https://lafronteraia.com" texto="Leer artículo completo →"]Título del artículo relacionado[/lfi-interlink]<!-- /wp:shortcode -->', ]); // Related posts grid register_block_pattern('lfi/related', [ 'title' => __('Artículos Relacionados', 'lfi-child'), 'description' => __('Grid de posts relacionados', 'lfi-child'), 'categories' => ['lfi'], 'content' => '<!-- wp:shortcode -->[lfi-related title="Artículos relacionados"]<div class="lfi-related-card"><a href="#"><h4>Título artículo</h4><p>Descripción breve</p></a></div><div class="lfi-related-card"><a href="#"><h4>Otro artículo</h4><p>Descripción breve</p></a></div>[/lfi-related]<!-- /wp:shortcode -->', ]); } // ============================================ // META BOX FOR CONTENT TYPE IN POST EDITOR // ============================================ add_action('add_meta_boxes', 'lfi_add_meta_box'); function lfi_add_meta_box() { add_meta_box('lfi_content_type', 'La Frontera IA — Tipo de Contenido', 'lfi_content_type_meta_box', 'post', 'side', 'default'); } function lfi_content_type_meta_box($post) { $current = get_post_meta($post->ID, '_lfi_content_type', true); $types = [ 'MONEY' => '💰 MONEY (Comparativa/Review)', 'TRAFFIC' => '📈 TRAFFIC (Noticia/Listado)', 'SUPPORTING' => '📚 SUPPORTING (Guía/Glosario/Recurso)', ]; wp_nonce_field('lfi_content_type_nonce', 'lfi_content_type_nonce'); echo '<select name="lfi_content_type" style="width:100%">'; echo '<option value="">— Sin clasificar —</option>'; foreach ($types as $k => $v) { echo sprintf('<option value="%s" %s>%s</option>', $k, selected($current, $k, false), $v); } echo '</select>'; // Reading time $reading = get_post_meta($post->ID, '_lfi_reading_time', true); echo '<p style="margin-top:12px;font-size:13px;color:#666;">Tiempo de lectura (min):</p>'; echo sprintf('<input type="number" name="lfi_reading_time" value="%s" min="1" max="60" style="width:80px">', esc_attr($reading ?: '5')); // Shortcodes disponibles - referencia rápida echo '<p style="margin-top:16px;font-size:12px;color:#888;line-height:1.5;">'; echo '<strong>Shortcodes disponibles:</strong><br>'; echo '<code>[lfi-box tipo="info"]</code><br>'; echo '<code>[lfi-cta id="hostinger"]</code><br>'; echo '<code>[lfi-table]</code><br>'; echo '<code>[lfi-review score="8.5"]</code><br>'; echo '<code>[lfi-best-pick]</code><br>'; echo '<code>[lfi-pros-cons]</code><br>'; echo '<code>[lfi-interlink url="..."]</code><br>'; echo '<code>[lfi-related]</code><br>'; echo '<code>[lfi-step number="1"]</code>'; echo '</p>'; } add_action('save_post', 'lfi_save_meta_box'); function lfi_save_meta_box($post_id) { if (!isset($_POST['lfi_content_type_nonce']) || !wp_verify_nonce($_POST['lfi_content_type_nonce'], 'lfi_content_type_nonce')) return; if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) return; if (!current_user_can('edit_post', $post_id)) return; if (isset($_POST['lfi_content_type'])) { update_post_meta($post_id, '_lfi_content_type', sanitize_text_field($_POST['lfi_content_type'])); } if (isset($_POST['lfi_reading_time'])) { update_post_meta($post_id, '_lfi_reading_time', intval($_POST['lfi_reading_time'])); } } // TEMPORAL: Instalar GeneratePress add_action('admin_init', function() { if (!isset($_GET['install_gp'])) return; // Obtener el zip subido (media ID 372) $file_path = get_attached_file(372); if (!$file_path || !file_exists($file_path)) { echo 'Zip not found'; return; } WP_Filesystem(); $theme_dir = get_theme_root(); $unzip = unzip_file($file_path, $theme_dir); if (is_wp_error($unzip)) { echo 'Error: ' . $unzip->get_error_message(); } else { echo 'GeneratePress instalado en: ' . $theme_dir . '/generatepress'; // Activar GeneratePress switch_theme('generatepress'); } exit; }); https://lafronteraia.com/post-sitemap.xml 2026-05-11T07:40:56+00:00 https://lafronteraia.com/page-sitemap.xml 2026-05-08T13:53:12+00:00 https://lafronteraia.com/category-sitemap.xml 2026-05-11T07:40:56+00:00 https://lafronteraia.com/post_tag-sitemap.xml 2026-05-10T07:25:42+00:00 https://lafronteraia.com/author-sitemap.xml 2026-05-08T22:38:20+00:00