投稿の一括操作で指定のカテゴリーを削除する

管理画面の[投稿|編集]では一覧されている投稿の情報を一括編集(bulk edit)できます。カテゴリー追加、タグ変更、ステータス変更、などなどまとめて変更することができて便利です。
が、微妙なのは「カテゴリー」。これは「追加」しかできません。長く運営しているブログで「このカテゴリー要らんかったわ」と思っても、何百何千も投稿があったら何ともしようがありません。一つずつ投稿を編集するほどヒマだと良いですが、それはまた別の意味でまったく良くないですね。
そんなわけで今回は、一括操作で指定した投稿から任意のカテゴリー達を一気に削除する機能を、一部イレギュラーなことをしつつ実現します。

元ネタは、本家 Forums です。何千もある投稿のカテゴリー間違いを手作業で直しているそうなので助けてあげましょう。

一括操作をフックできる所を探す

機能を拡張させるにはフックを使います。元々プラグイン用な雰囲気ですが、プラグイン作って配布したらサポートでエラいことになりそうで怖いです。プラグイン作者様ってすごい、尊敬するぜ。みんな積極的に donate しようぜ!

  1. [一括操作]で[投稿を更新]をクリックする。
  2. wp-admin/edit.php からスタート。ざっと見るとフックは使用されていないので、呼び出している関数を見てみます。
  3. 25行目 check_admin_referer('bulk-posts');
    edit.php が管理画面から実行されているかチェックする関数です。内部で以下のアクションを実行しています。 wp-includes/pluggable.php
    810行目 do_action('check_admin_referer', $action, $result);
  4. 89行目 $done = bulk_edit_posts($_GET);
    「一括操作」を処理する関数です。内部を見ても投稿を更新する wp_update_post までフックはありません。 wp-admin/includes/post.php
    307行目$updated[] = wp_update_post( $post_data );

使えるフックは check_admin_referer アクションだけです。これをフックしてカテゴリー削除しましょう。本来の目的とはまったく違う使い方なのでイレギュラーなカスタマイズです。ご利用は at your own risk でよろしく。

「カテゴリーを削除する」UI を追加する

熟慮の結果、カテゴリーチェックボックス群に[選択したカテゴリーを削除する]チェックボックスを追加する事にします。

  1. カテゴリーチェックボックス群は <ul class="cat-checklist"> のリストになっている。
  2. リストを出力しているのは wp-admin/includes/template.php 1108行目の wp_category_checklist。
  3. wp_category_checklist を調べて、とメンドクサくなってきたので javascript で追加することに決定。

admin-script.js を作成して以下を記述。

jQuery(document).ready(function($){
  $('.cat-checklist')
    .prepend(
      '<li>
        <label class="selectit"><input value="1" type="checkbox" 
          name="remove_categories" id="remove_categories"/> 選択したカテゴリーを削除する</label>
      </li>'
    );
});

そして functions.php に以下を追加します。

function my_enqueue() {
  wp_enqueue_script('my_admin_script', get_bloginfo('template_url') . '/admin-script.js', array('jquery'), false, true);
}
add_action('admin_init', 'my_enqueue');

これで[選択したカテゴリーを削除する]チェックボックスが表示されました。

選択されたカテゴリー達を選択された投稿達から削除する

以下を functions.php に追加すれば OK。一括操作で[選択したカテゴリーを削除する]といくつかのカテゴリーにチェックがあった場合にのみ、カテゴリー削除をします。

function my_bulk_edit($action, $result){
  if('bulk-posts' == $action && isset($_GET['remove_categories']) && isset($_GET['post_category'])){
    if(($_GET['action'] != -1 || $_GET['action2'] != -1 ) && ( isset($_GET['post']) || isset($_GET['ids']))){
      $post_ids = isset($_GET['post']) ? array_map( 'intval', (array) $_GET['post'] ) : explode(',', $_GET['ids']);
      $doaction = ($_GET['action'] != -1) ? $_GET['action'] : $_GET['action2'];
    }
    if('edit' == $doaction){
      if(isset($_GET['post_type']) && 'page' == $_GET['post_type']){
        if(! current_user_can('edit_pages'))
          wp_die( __('You are not allowed to edit pages.'));
      } else {
        if(! current_user_can( 'edit_posts' ))
	  wp_die( __('You are not allowed to edit posts.'));
      }
	  
      $post_IDs = array_map('intval', (array) $_GET['post']);
      if(is_array($_GET['post_category']) && ! empty($_GET['post_category'])){
	$remove_cats = array_map('absint', $_GET['post_category']);
	unset($_GET['post_category']);
        foreach($post_IDs as $post_ID){
	  if(wp_check_post_lock($post_ID)) continue;
	  $cats = (array) wp_get_post_categories($post_ID);
	  $post_data['post_category'] = array_diff($cats, $remove_cats);
	  $post_data['ID'] = $post_ID;
	  wp_update_post($post_data);
	}
      } 
    }
  }
}
add_action('check_admin_referer', 'my_bulk_edit', 10, 2);

チェックされたカテゴリーの ID は $_GET['post_category'] 配列に入っています。選択された各々の投稿について、その投稿のカテゴリー達と $_GET['post_category'] との差分(array_diff)で投稿のカテゴリーを更新します。unset($_GET['post_category']); することで、この後の通常の一括操作処理で 選択されたカテゴリーが「追加」されないようにします。

参照:Forum
Remove Categories with bulk edit
動作確認バージョン
  • WordPress 2.9.1

2 Comments

  • バージョン 3.2.1では
    function my_bulk_edit内の$_GETを$_REQUESTに変更すると動作しますよ。

    • kz kz

      きゃー!そうなんですね、ありがとうございます!!感謝☆