WordPressの記事のページ分割で次ページへのリンクテキストを指定できるようにする

WordPressで単一の記事をページ分割するには対応するテーマを使ったうえで以下のように書く。

<!--nextpage-->

さらにMultipageというプラグインを使うと次ページへのリンクテキストを指定できるようになる。ニュースサイトによくあるようなやつ。

ただこのMultipageで1.3系から1.5系の間に破壊的変更が行われたようでバージョンアップしたら大きく表示が崩れてしまった。そこでちょっとこれの利用はやめて、Multipageの1.3系を参考にテーマ内に自前で実装することにした。

なおブロックエディタではなくクラシックエディタでの対応。

やること

  • 記事内にリンクテキスト付きの改ページを表示する
  • 編集画面でリンクテキスト付きの改ページを扱えるようにする
    • 編集の際にビジュアルエディタでリンクテキスト付きの改ページを挿入できるようにする
    • ビジュアルエディタとテキストエディタを切り替えた際に改ページの内容が消えないようにする

記事内にリンクテキスト付きの改ページを表示するコード

nextpageショートコードを追加して利用するが、ショートコードとしては動作させずに表示前に正規表現を使って強引に書き換えるという手法を採っている。

functions.php

add_shortcode('nextpage', 'shortcode_nextpage');
function shortcode_nextpage($atts, $content = null)
{
    return ''; // do nothing ショートコードとして動く前にフィルターで書き換えられるためここの出力はどうでもいい
    // 管理画面でエディタにボタンとして登録するために必要だからこうしている?
}

add_action('wp', 'show_nextpage_link');
function show_nextpage_link()
{
    global $post;

    // 処理の必要なし
    if (is_feed() || is_404() || empty($post)) {
        return;
    }

    $page = get_query_var('page');
    $content = $post->post_content;
    $shortcode_pattern = "/\[nextpage[^\]]*\]/";
    $nextlink_titles = array();

    // 全てのnextpageショートコードをリンクをともなった<!--nextpage-->に書き換え
    preg_match_all($shortcode_pattern, $content, $matches);
    foreach ($matches[0] as $match) {
        $atts = shortcode_parse_atts(str_replace(array( '[', ']' ), '', $match));
        $nextpage_titles[] = $atts["title"];
    }

    // nextpageショートコードが存在しない場合は処理の必要なし
    if (empty($nextpage_titles)) {
        return;
    }

    // 次ページがあればタイトル付きのリンクを表示
    $current_page = empty($page) ? 1 : $page; // $pageがなければ1ページ目
    $next_page = $current_page + 1;
    $nextpage_url = get_subpage_link($next_page);

    $subpage_title_index = $current_page - 1;
    if (array_key_exists($subpage_title_index, $nextpage_titles)) {
        $nextpage_link = '<div class="article_next"><a href="' . $nextpage_url . '"><span class="mark_gt"></span> ' .  $nextpage_titles[$subpage_title_index] . '</a></div>';
    } else {
        // 最後のページのコンテンツの末尾に空のリンクが表示されないように
        $nextpage_link = '';
    }

    // WordPress本来の改ページを追加し、その直前にタイトル付きの次ページリンクを表示する
    // 全てのショートコードが同じ内容に変換されるが、本文は常に1ページ分しか表示されないので問題ない
    $post->post_content = preg_replace($shortcode_pattern, $nextpage_link . '<!--nextpage-->', $content);
}

function get_subpage_link($page) {
    if (!get_option('permalink_structure') || is_admin() || true == get_query_var('preview')) {
        return add_query_arg(array('page' => $page));
    }                    

    $base = get_permalink();            
    return trailingslashit($base) . user_trailingslashit($page, 'page');        
}

編集画面でリンクテキスト付きの改ページを扱えるようにするコード

JavaScriptでTinyMCEのプラグインを作成し、それをWordPressのフィルターで読み込む。

ビジュアルエディタで追加できるようにボタンも表示する。

なお一度追加した改ページのタイトルはビジュアルエディタからは変更できないので、テキストエディタに切り替えて編集するか、一度削除して挿入しなおす。

functions.php

add_filter('mce_buttons', 'insert_titled_page_break_register_buttons');
function insert_titled_page_break_register_buttons($buttons)
{
    array_push($buttons, 'titled_page_break');
    return $buttons;
}

add_filter('mce_external_plugins', 'insert_titled_page_break_register_tinymce_javascript');
function insert_titled_page_break_register_tinymce_javascript($plugin_array)
{
    $plugin_array['titled_page_break'] = get_template_directory_uri() . '/js/tinymce-plugin-titled-page-break.js';

    return $plugin_array;
}

js/tinymce-plugin-titled-page-break.js

(function() {
  tinymce.create("tinymce.plugins.TitledPageBreakButton", {
    init: function(ed, url) {
      ed.addButton("titled_page_break", {
        title: "改ページ",
        icon: "wp_page",
        cmd: "titled_page_break_cmd"
      });
      ed.addCommand("titled_page_break_cmd", function() {
        ed.windowManager.open({
          title: "改ページ",
          body: [
            {
              type: "textbox",
              name: "title",
              label: "Title",
              value: ""
            }
          ],
          onsubmit: function(e) {
            // Check for title length
            if (typeof e.data.title != "undefined" && e.data.title.length)
              shortcode = '[nextpage title="' + e.data.title + '"]<br />';

            ed.execCommand("mceInsertContent", 0, shortcode);
          }
        });
      });

      // ビジュアルエディタを表示する際にショートコードを改ページを示す画像に差し替えて表示する
      ed.on("BeforeSetContent", function(e) {
        if (e.content) {
          if (e.content.indexOf('[nextpage title="') !== -1) {
            e.content = e.content.replace(
              /\[nextpage title=\"(.*?)\"\]/g,
              function(match, nextpage_title) {
                return (
                  '<img src="' +
                  tinymce.Env.transparentSrc +
                  '" data-wp-more="subpage" class="wp-more-tag mce-wp-nextpage" ' +
                  'title="' +
                  nextpage_title +
                  '" data-mce-resize="false" data-mce-placeholder="1" />'
                );
              }
            );
          }
        }
      });

      // ビジュアルエディタを抜ける際に改ページを示す画像をショートコードに戻す
      ed.on("PostProcess", function(e) {
        if (e.get) {
          e.content = e.content.replace(/<img[^>]+>/g, function(image) {
            var match,
              nextpage_title = "";

            if (image.indexOf('data-wp-more="subpage"') !== -1) {
              if ((match = image.match(/title="([^"]+)"/))) {
                nextpage_title = match[1];
              }

              image = '[nextpage title="' + nextpage_title + '"]';
            }

            return image;
          });
        }
      });
    },
    createControl: function(n, cm) {
      return null;
    }
  });
  tinymce.PluginManager.add(
    "titled_page_break",
    tinymce.plugins.TitledPageBreakButton
  );
})();