続・連載記事のページネーション

この記事は1年以上前に書かれました。
内容が古くなっている可能性がありますのでご注意下さい。


以前開発した連載記事のページネーションのプラグインに、連載記事一覧を表示するウィジェットを追加した、Automatic Series Organizerプラグインを開発します。

仕様は極めて単純で、記事の投稿時に

series_idという決められた名前のカスタムフィールドを追加し、値の欄に連載名を記入しておくと、同じseries_idの値を持つ投稿を1つの連載記事としてまとめ、ページネーション

を表示するとともに、連載記事一覧をウィジェット

に表示するというものです。
プログラムの完全なリストは以下の通りです。

<?php
/*
  Plugin Name: Automatic Series Organizer
  Plugin URI: http://midoriit.com/works/auto-series.html
  Description: Automaticallly organize pagination and widgets from series of posts.
  Version: 0.3
  Author: Midori IT Office, LLC
  Author URI: http://midoriit.com/
  License: GPLv2 or later
  Text Domain: auto-series
  Domain Path: /languages/
*/

/*
 * Localization
*/
add_action( 'plugins_loaded', 'auto_series_loaded' );
function auto_series_loaded() {
  $ret = load_plugin_textdomain( 'auto-series', false,
    basename( dirname(__FILE__) ).'/languages/' );
}

/*
 * List of Series Widget
*/
add_action( 'widgets_init', function() {
  register_widget("Series_Widget");
});
class Series_Widget extends WP_Widget {

  // Constructor
  function __construct() {
    parent::__construct(
      'Series_Widget',
      __('Series List', 'auto-series'),
      array( 'description' => __( 'Show List of Series', 'auto-series' ), )
    );
  }

  // Widget option form
  function form($instance) {
    $title = esc_attr($instance['title']);
    echo '<p><label for="'.$this->get_field_id('title').'">';
    echo __( 'Title:', 'auto-series' );
    echo '<input class="widefat" id="'.$this->get_field_id('title').'" name="'. $this->get_field_name('title').'" type="text" value="'.$title.'" />';
    echo '</label></p>';
  }

  // Updaate widget option
  function update($new_instance, $old_instance) {
    $instance['title'] = strip_tags($new_instance['title']);
    return $instance;
  }

  // Output list of series
  function widget($args, $instance) {

    extract( $args );
    echo $before_widget;
    if( $instance['title'] ) {
      echo $before_title.$instance['title'].$after_title;
    } else {
      echo $before_title.__( 'Series List', 'auto-series' ).$after_title;
    }

    $args = array(
      'meta_key'=> 'series_id',
      'orderby' => 'date',
      'order' => 'ASC',
      'nopaging' => true );

    $series = array();

    $posts = new WP_Query( $args );
    if( $posts->found_posts > 1) {
      echo '<ul>';
      while( $posts->have_posts() ) {
        $posts->the_post();
        $series_id = get_post_meta(get_the_ID(), 'series_id', true);
        if( !in_array( $series_id, $series ) ) {
          echo '<li><a href="'.get_permalink().'">'.$series_id.'</a></li>';
          array_push($series, $series_id);
        }
      }
      echo '</ul>';
    }
    wp_reset_postdata();
    echo $after_widget;
  }
}

/*
 * CSS for Pagination
 */
add_action( 'wp_print_styles', 'pagination_css' );
function pagination_css() {
  $css_file = plugins_url( 'auto-series.css', __FILE__ );
  wp_enqueue_style( 'auto-series', $css_file );
}

/*
 * Show Pagination
 */
function series_pagination() {

  global $post;
  $current = $post->ID;
  $meta_value = get_post_meta($current, 'series_id', true);

  if( $meta_value == '' ) {
    return;
  }

  $args = array(
    'meta_key'=> 'series_id',
    'meta_value' => $meta_value,
    'orderby' => 'date',
    'order' => 'ASC',
    'nopaging' => true );

  $pagination='';
  $series = new WP_Query( $args );
  if( $series->found_posts > 1) {
    $num = 1;
    $pagination = '<div class="series_pagination"><span>'.$meta_value.'</span> <span>';
    while( $series->have_posts() ) {
      $series->the_post();
      if( $current == get_the_ID() ) {
        if( $num > 9 ) {
          $pagination = $pagination.'<a class="current_post wide">'.$num.'</a>';
        } else {
          $pagination = $pagination.'<a class="current_post">'.$num.'</a>';
        }
      } else {
        $permalink = esc_url( apply_filters( 'the_permalink', get_permalink() ) );
        if( $num > 9 ) {
          $pagination = $pagination.'<a class="wide" href="'.$permalink.'" title="'.get_the_title().'">'.$num.'</a>';
        } else {
          $pagination = $pagination.'<a href="'.$permalink.'" title="'.get_the_title().'">'.$num.'</a>';
        }
      }
      $num++;
    }
    $pagination = $pagination.'</span></div>';
  }
  wp_reset_postdata();

  echo $pagination;
}
?>

まずは、一丁前にローカライゼーションをしています。

add_action( 'plugins_loaded', 'auto_series_loaded' );
function auto_series_loaded() {
  $ret = load_plugin_textdomain( 'auto-series', false,
    basename( dirname(__FILE__) ).'/languages/' );
}

日本語の言語リソース(auto-series-ja.po)は以下のように定義し、

msgid "Midori IT Office, LLC"
msgstr "合同会社緑IT事務所"
msgid "Title:"
msgstr "タイトル:"
msgid "Series List"
msgstr "連載記事一覧"
msgid "Show List of Series"
msgstr "連載記事の一覧を表示します"
msgid "Automaticallly organize pagination and widgets from series of posts."
msgstr "連載記事のページネーションと連載一覧のウィジェットを自動的に構成します。"

以下のコマンドでバイナリファイル(auto-series-ja.mo)に変換します。

msgfmt -o auto-series-ja.mo auto-series-ja.po

msgfmtコマンドは、GetText for Windowsに含まれています。
次の

add_action( 'widgets_init', function() {
  register_widget("Series_Widget");
});

でウィジェットを登録します。ウィジェット本体は次のSeries_Widgetクラスです。ウィジェットのクラスは、WP_Widgetクラスを派生させて作ります。
コンストラクタ

   function __construct() {
    parent::__construct(
      'Series_Widget',
      __('Series List', 'auto-series'),
      array( 'description' => __( 'Show List of Series', 'auto-series' ), )
    );
  }

では、ウィジェット管理画面に表示される名前や説明を定義します。
次のform()メソッドは、ウィジェット管理画面でオプション設定をするためのフォームを表示します。

  function form($instance) {
    $title = esc_attr($instance['title']);
    echo '<p><label for="'.$this->get_field_id('title').'">';
    echo __( 'Title:', 'auto-series' );
    echo '<input class="widefat" id="'.$this->get_field_id('title').'" name="'. $this->get_field_name('title').'" type="text" value="'.$title.'" />';
    echo '</label></p>';
  }

ここでは、ウィジェットのタイトルを設定できるようにしています。
次のupdate()メソッドで値を更新します。

  function update($new_instance, $old_instance) {
    $instance['title'] = strip_tags($new_instance['title']);
    return $instance;
  }

HTMLのタグは取り除いています。
ウィジェットを表示するのはwidget()メソッドです。

  function widget($args, $instance) {

    extract( $args );
    echo $before_widget;
    if( $instance['title'] ) {
      echo $before_title.$instance['title'].$after_title;
    } else {
      echo $before_title.__( 'Series List', 'auto-series' ).$after_title;
    }

    $args = array(
      'meta_key'=> 'series_id',
      'orderby' => 'date',
      'order' => 'ASC',
      'nopaging' => true );

    $series = array();

    $posts = new WP_Query( $args );
    if( $posts->found_posts > 1) {
      echo '<ul>';
      while( $posts->have_posts() ) {
        $posts->the_post();
        $series_id = get_post_meta(get_the_ID(), 'series_id', true);
        if( !in_array( $series_id, $series ) ) {
          echo '<li><a href="'.get_permalink().'">'.$series_id.'</a></li>';
          array_push($series, $series_id);
        }
      }
      echo '</ul>';
    }
    wp_reset_postdata();
    echo $after_widget;
  }

ウィジェットの出力に先立ち$before_widgetを、最後に$after_widgetを出力しています。また、タイトルの前には$before_titleを、後には$after_titleを出力していますが、これらの変数はextract( $args );によって得られます。
WP_Query()でカスタムフィールドseries_idを持つ記事を検索し、series_idの値(つまり連載名)が、出力済み連載名を格納する配列$seriesに含まれていなければ、その記事へのリンク付きで連載名を出力し、連載名を配列$seriesに格納します。
記事の日付で昇順に検索しているため、連載名からリンクする記事は、その連載の最初の記事になります。また、古い連載から順に表示されます。

以降は、ページネーションのためのCSSの処理と、ページネーションを出力するseries_pagination()メソッドの定義です。これらは、以前開発した連載記事のページネーションのプラグインと基本的には同じです。但し、カスタムフィールドの名前がseries_idに変わっています。

今回作成したプラグインはGitHubからダウンロードできます。
https://github.com/midoriit/auto-series