かもメモ

自分の落ちた落とし穴に何度も落ちる人のメモ帳

WordPress 投稿のカテゴリーをラジオボタンにカスタマイズしたら、クイック編集でカテゴリーがチェックされなくなったので。

ちょっと古い記事なのですが、LIGさんの解決!WordPress投稿画面のカテゴリーをカスタマイズする方法という記事を参考に投稿のカテゴリーを複数選択できないようにラジオボタンに変更しました。

記事に掲載されているコードの通りに行うことでカテゴリー選択部分をチェックボックスからラジオボタンに変更することができたのですが、WordPressのクイック編集という機能を使った時にチェックしていたはずのカテゴリーのチェックが外れてしまうというバグに遭遇したので修正してみました。

LIGさんの記事を参考に投稿画面のカテゴリーの親カテゴリーを選択不可にして、ラジオボタンに変更する

WordPressのテーマ内にclassesというフォルダを作成しMy_Category_Checklist.phpというファイルを作成します。 ファイル名は別になんでも良いです
元の記事ではトップカテゴリーのみボタンを表示させないようにしていましたが、カテゴリーが複数ネストしても最終的なカテゴリーだけにラジオボタンが表示されるようにカスタマイズします。

<?php // My_Category_Checklist.php
/**
 *  My_Category_Checklist
 *    template.php 内にある Walker_Category_Checklist を継承したCLASS
 *    投稿画面のカテゴリーの親カテゴリーのチェックボックスを表示しない
 *    投稿画面のカテゴリーのチェックボックスをラジオボタンに変更
 */
require_once(ABSPATH . '/wp-admin/includes/template.php');

class My_Category_Checklist extends Walker_Category_Checklist {

  function start_el( &$output, $category, $depth = 0, $args = array(), $id = 0 ) {
    extract($args);
    if( empty($taxonomy) ) {
      $taxonomy = 'category';
    }
    if( $taxonomy == 'category' ) {
      $name = 'post_category';
    } else {
      $name = 'tax_input['.$taxonomy.']';
    }

    $class = in_array( $category->term_id, $popular_cats ) ? ' class="popular-category"' : '';
    // 親カテゴリの時はチェックボックス / ラジオボタンを表示しない
    // ※ 子カテゴリーがない場合もチェックボックスが消えてしまうので、子カテゴリーがある場合のみにラジオボタを表示させない
    $chaild_terms = get_term_children($category->term_id, $taxonomy);
    if( !empty( $chaild_terms ) ) {
      $output .= "\n<li id='{$taxonomy}-{$category->term_id}'$class>" .
        '<label class="selectit">' . esc_html( apply_filters('the_category', $category->name )) . '</label>';
    } else {
      $output .= "\n<li id='{$taxonomy}-{$category->term_id}'$class>" .
        '<label class="selectit"><input value="' . $category->term_id . '" type="radio" name="'.$name.'[]" id="in-'.$taxonomy.'-' . $category->term_id . '"' .
        checked( in_array( $category->term_id, $selected_cats ), true, false ) .
        disabled( empty( $args['disabled'] ), false, false ) . ' /> ' .
        esc_html( apply_filters('the_category', $category->name )) . '</label>';
    }
  }
}
?>

function.php内にアクションを追加して、カテゴリーが出力されている部分を先ほど作成したクラスに置き換えます。

<?php // function.php
// 先ほど作成したクラスのあるファイルを読み込み
require_once( dirname(__FILE__) . '/classes/My_Category_Checklist.php' );

function my_wp_category_terms_checklist_no_top_radio( $args, $post_id = null ) {
    $args['checked_ontop'] = false;
    $args['walker'] = new My_Category_Checklist();
    return $args;
}
add_action( 'wp_terms_checklist_args', 'my_wp_category_terms_checklist_no_top_radio' );

WrodPressの投稿画面で子カテゴリーのある親カテゴリーのチェックボックスが消え、チェックボックスラジオボタンに変更されました!
f:id:kikiki-kiki:20150608185024p:plain

これでカテゴリーにチェックを入れて記事を保存して、投稿一覧画面を開きます。
f:id:kikiki-kiki:20150608185947p:plain
問題なくカテゴリーが選択でき保存できていることが確認できます。

クイック編集をすると...

カテゴリーだけサクッと変更したい時など、この投稿一覧画面からクイック編集を行うこともよくあるかと思います。そこでクイック編集ボタンをクリックしてみると...
f:id:kikiki-kiki:20150608190712p:plain

ラジオボタンに変更しているとカテゴリーは保存されているのですが、クイック編集をした時にカテゴリーのチェックが外れてしまい、再度チェックをし直さないとチェックが外れた状態で上書き保存されてしまいます。カテゴリーを変更する以外のことをしていると気づかずにカテゴリーが外れるという事態になってしまうのです!
 

クイック編集時にチェックを入れている所を探す

調べているとクイック編集の時は/wp-admin/js/inline-edit-post.min.jsというjavascriptで投稿データをクイック編集の編集エリアに入れているようでした。同じ階層にmin化される前のinline-edit-post.jsがあったので、ソースを見てみるとinlineEditPostオブジェクトののeditという関数内にカテゴリーを選択する処理が書かれていました。

// /wp-admin/js/inline-edit-post.js
/* global inlineEditL10n, ajaxurl, typenow */

var inlineEditPost;
(function($) {
inlineEditPost = {
  /* 中略 */
  edit : function(id) {
    /* 中略 */
    
    // hierarchical taxonomies
    $('.post_category', rowData).each(function(){
      var taxname,
        term_ids = $(this).text();

      if ( term_ids ) {
        taxname = $(this).attr('id').replace('_'+id, '');
        $('ul.'+taxname+'-checklist :checkbox', editRow).val(term_ids.split(','));
      }
    });
  /* 略 */
});
}(jQuery));

このjs内で:checkbox決め打ちでカテゴリーのチェックが入るようになっているためにラジオボタンに変更するとクイック編集時にチェックがされなくなってしまっていたようです。  

クイック編集時にラジオボタンでもチェックが入るようにする。

原因が解ったので問題を解決していきます。
WordPressのコアには手を入れたくありません。ちょうどinlineEditPostオブジェクトがグローバルにあるのでこれを利用したいと思います。

テーマ内のjsフォルダにmy_admin.jsというファイルを作成します。
データの取り方などは元の関数に定義されていたものを参考にカテゴリーのチェックをラジオボタンでも行うようにします。

!function($) {
  if(inlineEditPost && inlineEditPost.edit) {
    // 元の edit の処理を取り出しておく
    var _Edit = inlineEditPost.edit;

    // edit 関数を書き換える
    inlineEditPost.edit = function(id) {
      // 元の edit の処理を行う
      _Edit.apply(inlineEditPost, arguments);
      
      var t = this, rowData, editRow;
      if ( typeof(id) === 'object' ) {
        id = this.getId(id);
      }
      // 編集エリアは既にcloneされているものを取得
      editRow = $('#edit-'+id);
      rowData = $('#inline_'+id);
      // hierarchical taxonomies
      $('.post_category', rowData).each(function(){
        var $t = $(this),
            taxname,
            term_ids = $t.text();
        if ( term_ids ) {
          taxname = $t.attr('id').replace('_'+id, '');
          // カテゴリーのラジオボタンにチェック
          $('ul.'+taxname+'-checklist :radio', editRow).val(term_ids.split(','));
        }
      });
      return false;
    };
  }
}(jQuery);

本当は カテゴリーを取得してチェックをしているループ内でラジオボタンのチェックも一緒に行ってあげるのが良いのだと思うのですが、edit関数が結構大きいのでデフォルトの処理を行った後改めて処理を追加する方法にしました。

後はこのjsを管理画面で読み込ませるようにします。

<?php // function.php
function add_myAdmin_scripts() {
  wp_enqueue_script('my-admin-script', get_bloginfo("template_directory") . '/js/my_admin.js', array('jquery'), '1.0', true);
}
add_action( 'admin_enqueue_scripts', 'add_myAdmin_scripts' );

管理画面のjsやcssを追加できるadmin_enqueue_scriptsフックを使って、jQueryが読み込まれていたらmy-admin.jsを読み込むようにします。

管理画面の投稿一覧画面をリロードして、クイック編集ボタンを押してみます。

f:id:kikiki-kiki:20150608204849p:plain \やりました/

ラジオボタンに変更してもカテゴリーの選択が外れないようにすることができました!  


[参考]

錦織敦史 アニメーションワークス Telegenic!

錦織敦史 アニメーションワークス Telegenic!