Função para apresentar uma lista das suas mensagens mais longas com sugestões para as dividir

No comments

 
Função para apresentar uma lista das suas mensagens mais longas com sugestões para as dividir

Se há alguns dias vimos como dividir publicações longas do WordPress em páginas sem afetar o SEO, hoje vamos adicionar uma função extra que lhe permite encontrar e categorizar facilmente essas publicações longas de acordo com o tipo de conteúdo, a duração e o idioma.

Quando as entradas longas são poucas, é fácil decorá-las quase de cor. Torna-se mais complicado quando há muitas e estas também têm as suas respectivas traduções noutras línguas. Este código tem como objetivo acelerar o processo de seleção ao dividir as mensagens (com o bloco "Quebra de Página" ou "Quebra de Página" do Gutenberg) e permitir-lhe saber rapidamente quais as que ainda tem de dividir e se as suas divisões actuais são coerentes.

O que é que o código faz exatamente?

O seguinte código adicionado ao functions.php do seu modelo ou tema filho irá gerar um shortcode, utilizando este shortcode em qualquer página ou post irá mostrar uma lista paginada (10 entradas por página) com todos os posts que têm mais de 1500 palavras, incluindo os traduzidos para outras línguas. Funciona tanto com Polylang como sem Polylang.

Estes valores podem ser alterados em // 2. Configuration (Não aumente demasiado o número total de páginas a apresentar se não quiser que a consulta abrande a resposta). A ideia é usar o shortcode em páginas de rascunho ou privadas para não correr o risco de um ataque de visitantes afetar o desempenho e consumir o CPU do seu servidor se tiver recursos limitados).

Este código permite-lhe ver não só quantas pausas tem uma publicação (ou se não tem nenhuma), mas também como as palavras estão distribuídas pelas diferentes secções, mostrando quantas palavras contém cada pausa, o que pode ser muito útil para avaliar se a sua paginação atual está equilibrada.

Deteção automática de tipos de conteúdo

Foi adicionada a deteção automática para 4 tipos de conteúdo, em que "pausa" significa uma pausa a cada x palavras. O cálculo baseia-se na densidade da informação e o limite máximo é configurável.

Técnica (700 palavras/intervalo): Tutoriais, código

Narrativa (1200 palavras/intervalo): Histórias, narrativas

Jornalística (900 palavras/intervalo): Notícias, artigos

Geral (1000 palavras/intervalo): Conteúdo padrão

Critérios de seleção

Para os critérios de código, procurei dicas e referências. Alguns parâmetros foram ajustados ao meu blogue, mas este é sempre elástico e pode ser ajustado a cada sítio, podendo sempre acrescentar mais alguns sinais para tornar a deteção mais precisa. Tome-o como ponto de partida e examine os elementos que utiliza mais frequentemente nos seus artigos, de acordo com o seu tipo, ou adicione as suas próprias marcas que o código pode identificar e algumas das etiquetas que utiliza com mais frequência.

Os critérios actuais de seleção são os seguintes:

A. Conteúdo técnico (⚙️ 700 palavras/breve)

  • Categorias: tutorial, guia, técnico, codificação, programação
  • Tags: código, software, tecnologia, desenvolvimento
  • Estrutura
    :Marcador de posição Polylang não modificar

B. Conteúdo narrativo (📖 1200 palavras/parte)

  • Categorias: conto, história, narrativa, literatura
  • Etiquetas: conto, romance, ficção, poesia
  • Estrutura
    :Marcador de posição Polylang não modificar

C. Conteúdo jornalístico (📰 900 palavras/breve)

  • Categorias: notícias, atualidade, reportagem, artigo
  • Tags: imprensa, entrevista, opinião
  • Estrutura
    :Marcador de posição Polylang não modificar

D. Conteúdo geral (✍️ 1000 palavras/breve)

  • Aplica-se quando não coincide com os critérios anteriores.
  • Valor por defeito para os postos standard

O código avalia os itens pela seguinte ordem:

  1. Categorias de correio
  2. Etiquetas de publicação
  3. Estrutura do conteúdo
  4. Atribuir "Geral" se não houver correspondência

O número máximo de pausas sugerido é de 5 por publicação.

Aspeto

O CSS está incluído no código por conveniência e devolve este aspeto aos separadores. A informação que apresenta é bastante explícita.

Pode modificá-lo para ligar o título à página de edição, se achar mais conveniente para um acesso rápido, ou fazer quaisquer outras melhorias de que se lembre.

Código

// ======================================================================
//Displays a paginated list of long posts (ordered from most to least number of words) with suggestions for splitting them into pages according to content type
// Shortcode for Page Breaks analysis on long posts. Courtesy of /jrmora.com
// ======================================================================
function analizador_pagebreaks_completo() {
    // 1. Protección contra ejecución en admin/AJAX
    if (is_admin() || wp_doing_ajax() || (defined('REST_REQUEST') && REST_REQUEST)) {
        return '';
    }

    // 2. Configuración
    $config = [
        'umbral_palabras' => 1500,
        'posts_por_pagina' => 10,
        'mostrar_todos_idiomas' => true
    ];

    // 3. Detección de tipo de contenido
    function detectar_tipo_contenido($post_id) {
        $post = get_post($post_id);
        $content = strip_tags($post->post_content);
        
        // Por categorías
        $categorias = wp_get_post_categories($post_id, ['fields' => 'slugs']);
        if (array_intersect($categorias, ['tutorial', 'guia', 'tecnico', 'code', 'programacion'])) {
            return ['tipo' => 'técnico', 'color' => '#e3f2fd', 'icono' => '⚙️'];
        } elseif (array_intersect($categorias, ['relato', 'historia', 'narrativa', 'literatura'])) {
            return ['tipo' => 'narrativo', 'color' => '#f3e5f5', 'icono' => '📖'];
        } elseif (array_intersect($categorias, ['noticia', 'actualidad', 'reportaje', 'articulo'])) {
            return ['tipo' => 'periodístico', 'color' => '#e8f5e9', 'icono' => '📰'];
        }
        
        // Por etiquetas
        $etiquetas = wp_get_post_tags($post_id, ['fields' => 'slugs']);
        if (array_intersect($etiquetas, ['codigo', 'software', 'tecnologia', 'dev'])) {
            return ['tipo' => 'técnico', 'color' => '#e3f2fd', 'icono' => '⚙️'];
        } elseif (array_intersect($etiquetas, ['cuento', 'novela', 'ficcion', 'poesia'])) {
            return ['tipo' => 'narrativo', 'color' => '#f3e5f5', 'icono' => '📖'];
        }
        
        // Por estructura
        if (preg_match_all('/```|<pre>|<code>/i', $content) > 2) {
            return ['tipo' => 'técnico', 'color' => '#e3f2fd', 'icono' => '⚙️'];
        } elseif (preg_match_all('/\n—|\n"|\n\d+\. /i', $content) > 3) {
            return ['tipo' => 'narrativo', 'color' => '#f3e5f5', 'icono' => '📖'];
        } elseif (preg_match_all('/\n### |\n#### |\*Nota:/i', $content) > 3) {
            return ['tipo' => 'periodístico', 'color' => '#e8f5e9', 'icono' => '📰'];
        }
        
        return ['tipo' => 'general', 'color' => '#f5f5f5', 'icono' => '✍️'];
    }

    // 4. Cálculo de breaks por tipo
    function calcular_breaks_por_tipo($palabras, $tipo) {
        $config = [
            'técnico' => 700,
            'narrativo' => 1200,
            'periodístico' => 900,
            'general' => 1000
        ];
        
        $breaks = max(1, floor($palabras / ($config[$tipo] ?? 1000)) - 1);
        return min($breaks, 5); // Máximo 5 breaks
    }

    // 5. Función para calcular distribución de palabras entre breaks
    function calcular_distribucion_palabras($content) {
        $total_palabras = str_word_count(strip_tags($content));
        $breaks = substr_count($content, '<!--nextpage-->');
        
        if ($breaks === 0) {
            return [];
        }
        
        // Dividir el contenido por los breaks
        $secciones = explode('<!--nextpage-->', $content);
        $distribucion = [];
        
        foreach ($secciones as $seccion) {
            $palabras_seccion = str_word_count(strip_tags($seccion));
            $distribucion[] = $palabras_seccion;
        }
        
        return $distribucion;
    }

    // 6. Función para obtener todos los IDs de posts a procesar
    function obtener_ids_posts_a_procesar($mostrar_todos_idiomas) {
        $args = [
            'post_type' => 'post',
            'posts_per_page' => -1,
            'fields' => 'ids',
            'post_status' => 'publish'
        ];

        // Si Polylang está activo y queremos mostrar todos los idiomas
        if ($mostrar_todos_idiomas && function_exists('pll_get_post_translations')) {
            $query = new WP_Query($args);
            $all_post_ids = [];
            
            foreach ($query->posts as $post_id) {
                $translations = pll_get_post_translations($post_id);
                $all_post_ids = array_merge($all_post_ids, array_values($translations));
            }
            
            return array_unique($all_post_ids);
        }
        
        // Caso normal (sin Polylang o no mostrar todos los idiomas)
        $query = new WP_Query($args);
        return $query->posts;
    }

    // 7. Obtener y procesar posts
    $posts_procesados = [];
    $post_ids = obtener_ids_posts_a_procesar($config['mostrar_todos_idiomas']);

    foreach ($post_ids as $post_id) {
        procesar_post_con_tipo($post_id, $config['umbral_palabras'], $posts_procesados);
    }

    // 8. Ordenar por palabras (mayor a menor)
    usort($posts_procesados, function($a, $b) {
        return $b['palabras'] - $a['palabras'];
    });

    // 9. Paginación manual
    $paged = max(1, get_query_var('paged'));
    $total_posts = count($posts_procesados);
    $total_paginas = ceil($total_posts / $config['posts_por_pagina']);
    $offset = ($paged - 1) * $config['posts_por_pagina'];
    $posts_paginados = array_slice($posts_procesados, $offset, $config['posts_por_pagina']);

    // 10. CSS con mejoras visuales
    $output = '<style>
    .pb-ultimate-container {
        max-width: 800px;
        margin: 0 auto;
        font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
    }
    .pb-ultimate-item {
        background: #fff;
        border-radius: 8px;
        padding: 20px;
        margin-bottom: 20px;
        box-shadow: 0 1px 3px rgba(0,0,0,0.08);
        border: 1px solid #e0e0e0;
    }
    .pb-ultimate-title {
        font-size: 1.25rem;
        margin: 0 0 10px 0;
        line-height: 1.4;
    }
    .pb-ultimate-meta {
        display: flex;
        flex-wrap: wrap;
        gap: 12px;
        margin-bottom: 12px;
        font-size: 0.85rem;
    }
    .pb-ultimate-meta-item {
        display: inline-flex;
        align-items: center;
        gap: 4px;
    }
    .pb-ultimate-type {
        padding: 2px 10px;
        border-radius: 10px;
        font-size: 0.75rem;
        font-weight: 500;
    }
    .pb-ultimate-suggestion {
        background: #f5f5f5;
        border-left: 3px solid #64b5f6;
        padding: 10px 15px;
        margin-top: 15px;
        border-radius: 0 4px 4px 0;
        font-size: 0.9rem;
    }
    .pb-ultimate-pagination {
        display: flex;
        justify-content: center;
        margin: 30px 0;
        flex-wrap: wrap;
        gap: 8px;
    }
    .pb-ultimate-page {
        padding: 8px 16px;
        border-radius: 4px;
        text-decoration: none;
    }
    .pb-ultimate-page-number {
        border: 1px solid #e0e0e0;
        color: #1976d2;
    }
    .pb-ultimate-page-number:hover {
        background: #e3f2fd;
    }
    .pb-ultimate-page-current {
        background: #1976d2;
        color: white;
        border: 1px solid #1976d2;
    }
    .pb-ultimate-wordcount {
        font-weight: 600;
        color: #212121;
    }
    .pb-ultimate-language {
        background: #e3f2fd;
        color: #1565c0;
        padding: 2px 10px;
        border-radius: 10px;
        font-size: 0.75rem;
    }
    .pb-ultimate-warning {
        color: #d32f2f;
        font-weight: 500;
    }
    .pb-ultimate-distribution {
        margin-left: 5px;
        font-size: 0.95em; /* Aumentado de 0.8em a 0.95em */
        font-weight: 600; /* Negrita añadida */
        color: #444; /* Color más oscuro para mejor contraste */
        background: #f8f8f8; /* Fondo sutil */
        padding: 2px 6px;
        border-radius: 4px;
        border-left: 2px solid #64b5f6; /* Borde izquierdo azul */
    }
    .pb-ultimate-distribution-separator {
        color: #999;
        font-weight: normal;
        margin: 0 3px;
    }
    </style>';

    $output .= '<div class="pb-ultimate-container">';

    if (!empty($posts_procesados)) {
        foreach ($posts_paginados as $post) {
            $breaks_sugeridos = calcular_breaks_por_tipo($post['palabras'], $post['tipo']['tipo']);
            $palabras_por_seccion = ceil($post['palabras'] / ($breaks_sugeridos + 1));
            
            $output .= '<div class="pb-ultimate-item">';
            $output .= '<h3 class="pb-ultimate-title"><a href="' . esc_url($post['enlace']) . '">' . esc_html($post['titulo']) . '</a></h3>';
            
            $output .= '<div class="pb-ultimate-meta">';
            $output .= '<span class="pb-ultimate-meta-item pb-ultimate-wordcount">📝 ' . number_format($post['palabras']) . ' palabras</span>';
            $output .= '<span class="pb-ultimate-meta-item pb-ultimate-type" style="background: ' . $post['tipo']['color'] . '">';
            $output .= $post['tipo']['icono'] . ' ' . ucfirst($post['tipo']['tipo']);
            $output .= '</span>';
            $output .= '<span class="pb-ultimate-meta-item pb-ultimate-language">' . strtoupper($post['idioma']) . '</span>';
            
            if ($post['tiene_break']) {
                $distribucion = calcular_distribucion_palabras($post['contenido']);
                $distribucion_text = '';
                
                if (!empty($distribucion)) {
                    $distribucion_text = '<span class="pb-ultimate-distribution">(';
                    $distribucion_parts = [];
                    foreach ($distribucion as $index => $palabras) {
                        $distribucion_parts[] = ($index + 1) . 'º: ' . number_format($palabras);
                    }
                    $distribucion_text .= implode('<span class="pb-ultimate-distribution-separator"> · </span>', $distribucion_parts) . ')</span>';
                }
                
                $output .= '<span class="pb-ultimate-meta-item">✅ ' . $post['breaks_actuales'] . ' breaks ';
                $output .= $distribucion_text;
                $output .= '</span>';
            } else {
                $output .= '<span class="pb-ultimate-meta-item pb-ultimate-warning">⚠️ SIN PAGINAR</span>';
            }
            
            $output .= '</div>';
            
            $output .= '<div class="pb-ultimate-suggestion">';
            $output .= '<strong>Sugerencia:</strong> ' . $breaks_sugeridos . ' breaks (1 cada ~' . number_format($palabras_por_seccion) . ' palabras)';
            $output .= '</div>';
            
            $output .= '</div>';
        }

        // Paginación
        if ($total_paginas > 1) {
            $output .= '<div class="pb-ultimate-pagination">';
            $output .= paginate_links([
                'base' => str_replace(999999999, '%#%', esc_url(get_pagenum_link(999999999))),
                'format' => '?paged=%#%',
                'current' => $paged,
                'total' => $total_paginas,
                'prev_text' => __('&laquo;'),
                'next_text' => __('&raquo;'),
                'mid_size' => 1
            ]);
            $output .= '</div>';
        }
    } else {
        $output .= '<div class="pb-ultimate-item">No se encontraron posts que requieran paginación.</div>';
    }

    return $output . '</div>';
}

// Función auxiliar: Procesar post con tipo
function procesar_post_con_tipo($post_id, $umbral, &$posts) {
    $post = get_post($post_id);
    if (!$post) return;

    $content = $post->post_content;
    $word_count = str_word_count(strip_tags($content));
    
    if ($word_count >= $umbral) {
        $tipo = detectar_tipo_contenido($post_id);
        $idioma = 'es'; // Valor por defecto
        
        // Detectar idioma si Polylang está activo
        if (function_exists('pll_get_post_language')) {
            $idioma = pll_get_post_language($post_id) ?: 'es';
        }
        
        $posts[] = [
            'ID' => $post_id,
            'titulo' => $post->post_title,
            'palabras' => $word_count,
            'contenido' => $content,
            'enlace' => get_permalink($post_id),
            'tiene_break' => (strpos($content, '<!--nextpage-->') !== false),
            'breaks_actuales' => substr_count($content, '<!--nextpage-->'),
            'idioma' => $idioma,
            'tipo' => $tipo
        ];
    }
}

add_shortcode('analizador_pagebreaks_ultimate', 'analizador_pagebreaks_completo');

Suscríbete para recibir los post en tu email sin publicidad

Artigos relacionados

Deixe um comentário

Este blog se aloja en LucusHost

LucusHost, el mejor hosting