最長記事のリストを表示し、分割を提案する機能

コメントはありません

 
最長記事のリストを表示し、分割を提案する機能

数日前、SEOに影響を与えることなくWordPressの長い 投稿をページに分割する方法をご紹介しましたが、今日は、コンテンツの種類、長さ、言語に応じて長い投稿を簡単に検索し、分類できる機能を追加します。

長文項目が数個しかない場合は、ほぼ暗記するのは簡単だ。しかし、その数が多く、他の言語への翻訳もある場合は、より複雑になります。このコードは、(Gutenbergの "Page Break "または "Page Break "ブロックを使って)投稿を分割する際の選択プロセスをスピードアップさせ、まだ分割しなければならない投稿や、現在の分割が首尾一貫しているかどうかを素早く知らせることを目的としています。

コードは具体的に何をするのか?

以下のコードをテンプレートまたは子テーマのfunctions.phpに追加すると、ショートコードが生成されます。このショートコードを任意のページまたは投稿で使用すると、他の言語に翻訳されたものを含む1500語以上のすべての投稿がページ分割されたリスト(1ページあたり10エントリ)で表示されます。Polylangを使用しても、Polylangを使用しなくても動作します。

これらの値は// 2.設定 で変更することができます(ただ、クエリーでレスポンスを遅くしたくない場合は、表示するページの総数を増やしすぎないようにしてください)。リソースが限られている場合、訪問者のヒットがパフォーマンスに影響を与えたり、サーバーのCPUを食いつぶしたりするリスクを回避するために、下書きページやプライベートページでショートコードを使用することです)。

このコードによって、投稿にいくつの区切りがあるか(あるいはまったくないか)だけでなく、それぞれの区切りが何語含まれているかを表示することによって、異なるセクション間で単語がどのように配分されているかを見ることができる。

コンテンツタイプの自動検出

4つのコンテンツタイプに自動検出機能が追加されました。"break "は、xワードごとの区切りを意味します。計算は情報密度に基づいており、上限は設定可能です。

テクニカル(700ワード/休憩):チュートリアル、コード

ナラティブ(1200語/区切り):物語、語り

ジャーナリスティック(900ワード/区切り):ニュース、記事

一般(1000ワード/休憩):標準的な内容

選考基準

コードの基準については、ヒントや参考文献を探した。いくつかのパラメータは私のブログに合わせて調整したが、これは常に弾力的であり、サイトごとに微調整することができる。これを出発点として、記事の中で最も頻繁に使用する要素をその種類に応じて調べたり、コードが識別できる独自のブランドや最も頻繁に使用するタグのいくつかを追加してください。

現在の審査基準は以下の通り:

A.テクニカルコンテンツ(⚙️ 700ワード/区切り)

  • カテゴリー: チュートリアル, ガイド, テクニカル, コーディング, プログラミング
  • タグ: コード, ソフトウェア, テクノロジー, 開発
  • 構造
    :ポリラン・プレースホルダは変更しない

B.物語的な内容(📖1200ワード/休憩)

  • カテゴリー: ショートストーリー, 歴史, 物語, 文学
  • タグ: ショートストーリー, 小説, フィクション, 詩
  • 構造
    :ポリラン・プレースホルダは変更しない

C.ジャーナリスティックな内容(💐9000字/区切り)

  • カテゴリー: ニュース, 時事問題, レポート, 記事
  • Tags: プレス, オピニオン, インタビュー
  • 構造
    :ポリラン・プレースホルダは変更しない

D.一般的な内容(✍️ 1000語/区切り)

  • 上記の基準と一致しない場合に適用される。
  • 標準投稿のデフォルト値

コードは以下の順序で項目を評価する:

  1. 投稿カテゴリー
  2. 投稿タグ
  3. コンテンツの構成
  4. 一致するものがなければ「一般」を割り当てる

休憩の回数は1記事につき5回までとする。

アスペクト

CSSは便宜上コードに含まれており、タブにこのような外観を返す。表示される情報は非常に明確である。

タイトルを編集ページにリンクするように変更することもできます。

コード

// ======================================================================
//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

関連記事

コメントする

Este blog se aloja en LucusHost

LucusHost, el mejor hosting