WP Post Branches をカスタム投稿タイプに対応させる方法

WP Post Branches という便利なプラグインをご存じでしょうか。公開済みの投稿などの下書きの複製を作成し、公開記事には影響せずに下書き状態での更新や予約投稿機能を使って、時限更新ができるという優れもので、私も度々愛用しています。

wp-post-branches

ただし、ご覧のようにまだプラグインのヘッダー画像がないので、我こそはと思われる方は、ぜひ確認ちゃんに、いきなり送りつけてあげてください。素材がないよという方は、ここらへんをご自由に使っていただいて構いません。

ちなみに、WP Post Branches の仕組みを簡単に説明すると

  1. 公開済みの投稿を複製し下書き状態の投稿(ブランチ)を作る
  2. 公開中の投稿はそのままの状態で、ブランチを編集
  3. ブランチの公開時に、ブランチの内容を元の投稿にマージし、ブランチは削除

といった感じとなります。

branch-flow

固定ページを更新するような場合には、非常に便利なのですが、現バージョン(2.2)では、カスタム投稿タイプに対応できておらず、ブランチの作成はできるものの、公開時に元の記事にマージされず、そのまま公開されてしまいます。これは、プラグインのブランチ記事の公開処理が投稿と固定ページの時のみにしか実行されず、カスタム投稿タイプでは、通常の公開処理になってしまうためです。

add_action( 'publish_page', 'wpbs_save_post', 9999, 2 );
add_action( 'publish_post', 'wpbs_save_post', 9999, 2 );

カスタム投稿タイプに対応させるためには、上記フックの publish_xxxxxx の xxxxx をカスタム投稿タイプのスラッグにすればよいので、functions.php に

add_action( 'publish_book', 'wpbs_save_post', 9999, 2 );

などと、追記すれば book 投稿タイプでも問題なく利用できます。

ただし、この記述だと投稿タイプ毎に記述しなくてはならないので、手間になりがちです。
追加したカスタム投稿タイプを全てサポートさせるのであれば、以下のような記述となります。init で行っているのは、register_post_type の実行タイミングとして、init フック以降が推奨されているためです。

function add_wpbs_save_post_hooks() {
	// デフォルト以外で、show_uiがtrue(管理画面が有効)となっている、追加されたカスタム投稿タイプを取得
	$additional_post_types = get_post_types( array( '_builtin' => false, 'show_ui' => true ) );
	foreach ( $additional_post_types as $post_type ) {
		// 追加されたカスタム投稿ごとにフックを追加
		add_action( 'publish_' . $post_type, 'wpbs_save_post', 9999, 2 );
	}
}
add_action( 'init', 'add_wpbs_save_post_hooks', 9999 );

ただし、せっかくブランチができるのですから、できればプラグインのデフォルトで、カスタム投稿タイプに対応してほしいですよね。某案件では、プラグイン名を変更した上で以下のように変更してみました。

//add_action( 'publish_page', 'wpbs_save_post', 9999, 2 );
//add_action( 'publish_post', 'wpbs_save_post', 9999, 2 );
add_action( 'transition_post_status', 'wpbs_save_post', 9999, 3 );
function wpbs_save_post( $new_status, $old_status, $post ) {

	if ( $post->post_status != 'publish' ) { return; }
	if ( $org_id = get_post_meta( $post->ID, '_wpbs_pre_post_id', true ) ) {
		// post
		$new = array(
			'ID' => $org_id,
			'post_author' => $post->post_author,
			'post_date' => $post->post_date,
			'post_date_gmt' => $post->post_date_gmt,
			'post_content' => $post->post_content,
			'post_title' => $post->post_title,
			'post_excerpt' => $post->post_excerpt,
			'post_status' => 'publish',
			'comment_status' => $post->comment_status,
			'ping_status' => $post->ping_status,
			'post_password' => $post->post_password,
//			'post_name' => $post->post_name,
			'to_ping' => $post->to_ping,
			'pinged' => $post->pinged,
			'post_modified' => $post->post_modified,
			'post_modified_gmt' => $post->post_modified_gmt,
			'post_content_filtered' => $post->post_content_filtered,
			'post_parent' => $post->post_parent,
			'guid' => $post->guid,
			'menu_order' => $post->menu_order,
			'post_type' => $post->post_type,
			'post_mime_type' => $post->post_mime_type
		);
		wp_update_post( apply_filters( 'wpbs_draft_to_publish_update_post', $new ) );


		//postmeta
		$keys = get_post_custom_keys( $post->ID );

		$custom_field = array();
		foreach ( (array) $keys as $key ) {
			if ( preg_match( '/^_feedback_/', $key ) )
				continue;

			if ( preg_match( '/_wpbs_pre_post_id/', $key ) )
				continue;

			if ( preg_match( '/_wp_old_slug/', $key ) )
				continue;
				
			$key = apply_filters( 'wpbs_draft_to_publish_postmeta_filter', $key );

			delete_post_meta( $org_id, $key );
			$values = get_post_custom_values($key, $post->ID );
			foreach ( $values as $value ) {
				add_post_meta( $org_id, $key, $value );
			}
		}


		//attachment
//		$args = array( 'post_type' => 'attachment', 'numberposts' => -1, 'post_status' => null, 'post_parent' => $org_id );
//		$attachments = get_posts( $args );
//		if ($attachments) {
//			foreach ( $attachments as $attachment ) {
//				wp_delete_post( $attachment->ID );
//			}
//		}

		$args = array( 'post_type' => 'attachment', 'numberposts' => -1, 'post_status' => null, 'post_parent' => $post->ID ); 
		$attachments = get_posts( $args );
		if ($attachments) {
			foreach ( $attachments as $attachment ) {
				$new = array(
					'post_author' => $attachment->post_author,
					'post_date' => $attachment->post_date,
					'post_date_gmt' => $attachment->post_date_gmt,
					'post_content' => $attachment->post_content,
					'post_title' => $attachment->post_title,
					'post_excerpt' => $attachment->post_excerpt,
					'post_status' => $attachment->post_status,
					'comment_status' => $attachment->comment_status,
					'ping_status' => $attachment->ping_status,
					'post_password' => $attachment->post_password,
					'post_name' => $attachment->post_name,
					'to_ping' => $attachment->to_ping,
					'pinged' => $attachment->pinged,
					'post_modified' => $attachment->post_modified,
					'post_modified_gmt' => $attachment->post_modified_gmt,
					'post_content_filtered' => $attachment->post_content_filtered,
					'post_parent' => $draft_id,
					'guid' => $attachment->guid,
					'menu_order' => $attachment->menu_order,
					'post_type' => $attachment->post_type,
					'post_mime_type' => $attachment->post_mime_type,
					'comment_count' => $attachment->comment_count
				);
				$new = apply_filters( 'wpbs_pre_draft_to_publish_attachment', $new );
				$attachment_newid = wp_insert_post( $new );
				$keys = get_post_custom_keys( $attachment->ID );

				$custom_field = array();
				foreach ( (array) $keys as $key ) {
					$value = get_post_meta( $attachment->ID, $key, true );

					delete_post_meta( $org_id, $key );
					add_post_meta( $org_id, $key, $value );
				}
			}
		}


		//tax
		$taxonomies = get_object_taxonomies( $post->post_type );
		foreach ($taxonomies as $taxonomy) {
			$post_terms = wp_get_object_terms($post->ID, $taxonomy, array( 'orderby' => 'term_order' ));
			$post_terms = apply_filters( 'wpbs_pre_draft_to_publish_taxonomies', $post_terms );
			$terms = array();
			for ($i=0; $i<count($post_terms); $i++) {
				$terms[] = $post_terms[$i]->slug;
			}
			wp_set_object_terms($org_id, $terms, $taxonomy);
		}

	wp_delete_post( $post->ID );
	wp_safe_redirect( admin_url( '/post.php?post=' . $org_id . '&action=edit&message=1' ) );
	exit;
	}
}

あと、ブランチの都度に投稿にひも付いたメディアが増えてしまうのがいやだったので、wpbs_pre_post_update 関数内の、メディアを複製する処理をコメントアウトして利用しています。

/*
		//attachment
		$args = array( 'post_type' => 'attachment', 'numberposts' => -1, 'post_status' => null, 'post_parent' => $id ); 
		$attachments = get_posts( $args );
		if ($attachments) {
			foreach ( $attachments as $attachment ) {
				$new = array(
					'post_author' => $attachment->post_author,
					'post_date' => $attachment->post_date,
					'post_date_gmt' => $attachment->post_date_gmt,
					'post_content' => $attachment->post_content,
					'post_title' => $attachment->post_title,
					'post_excerpt' => $attachment->post_excerpt,
					'post_status' => $attachment->post_status,
					'comment_status' => $attachment->comment_status,
					'ping_status' => $attachment->ping_status,
					'post_password' => $attachment->post_password,
					'post_name' => $attachment->post_name,
					'to_ping' => $attachment->to_ping,
					'pinged' => $attachment->pinged,
					'post_modified' => $attachment->post_modified,
					'post_modified_gmt' => $attachment->post_modified_gmt,
					'post_content_filtered' => $attachment->post_content_filtered,
					'post_parent' => $draft_id,
					'guid' => $attachment->guid,
					'menu_order' => $attachment->menu_order,
					'post_type' => $attachment->post_type,
					'post_mime_type' => $attachment->post_mime_type,
					'comment_count' => $attachment->comment_count
				);
				$new = apply_filters( 'wpbs_pre_publish_to_draft_attachment', $new );
				$attachment_newid = wp_insert_post( $new );
				$keys = get_post_custom_keys( $attachment->ID );

				$custom_field = array();
				foreach ( (array) $keys as $key ) {
					$value = get_post_meta( $attachment->ID, $key, true );

				add_post_meta( $attachment_newid, $key, $value );
				}
			}
		}
*/

あと、希望を言えば、管理画面の記事一覧表示で、ブランチかどうか、どの記事のブランチかなどの情報が表示されると、もっと使いやすくなるのではーと思ってます。

WordPress の新規追加の権限を切り分けてみる

はい、どーも。

ユーザーに、既存記事の編集などはさせたいけれども、新規追加はさせたくない、例えば、編集者権限で記事の追加を行い、投稿者で編集のみを担当させるなんて場合に使える、いつ使うかわからないようなニッチな内容を紹介します。

“WordPress の新規追加の権限を切り分けてみる” の続きを読む

投稿の個別ページのみパーマリンクを変更する方法

WordPress で投稿のURLは、 /article/%postname%/ とか /blog/%post_id%/ にしたいけど、パーマリンク設定で article や blog といったディレクトリを入れてしまうと、カテゴリーやアーカイブの表示でも、このディレクトリが付いてしまってかっこ悪いですよね。

今回は、このちょっとしたモヤモヤを解消するべく、投稿の詳細表示の時のみ特定のディレクトリを追加する方法を紹介します。

“投稿の個別ページのみパーマリンクを変更する方法” の続きを読む

アイキャッチ画像の色を取得して背景色に設定する・改

[WordPress] アイキャッチ画像の色を取得して背景色に設定する の「画像の指定座標の色を取得する」の部分がURLになってしまっていて、httpリクエストが発生してしまうことになるので、内部パスで取る方法。

$thumbnail_id = get_post_thumbnail_id($post->ID);
$thumbnail_id = 18;
$dirpath = wp_upload_dir();
$image = image_get_intermediate_size( $thumbnail_id, 'post-thumbnail' );
$image_path = $dirpath['basedir'] . '/' . $image['path'];
switch ( $image['mime-type'] ) {
	case 'image/jpeg' :
		$image_resource = imagecreatefromjpeg( $image_path );
		break;
	case 'image/png' :
		$image_resource = imagecreatefrompng( $image_path );
		break;	
	case 'image/gif' :
		$image_resource = imagecreatefromgif( $image_path );
		break;
	default :
		return false;
}
$rgb = imagecolorat( $image_resource, 1, 1 );
$info = imagecolorsforindex( $image_resource, $rgb );

あとは、同じ

WordPressで特定の関数から呼ばれたときだけフックをかける裏技!?

フォーラムの 「カテゴリのよく使うものを削除」の回答にて使ったものなのだけど、特定の関数から呼ばれたときだけフックさせたい時ってありませんか?私は結構あります。

自作のプラグインとか、テーマであれば、直前に add_filter して、直後に remove_filter することも出来ますが、コアファイルや配布されているプラグイン・テーマだとそうもいかないですよね。

そんなにっちもさっちも行かないようなケースの場合、php の debug_backtrace 関数を使って、呼び出し元に特定の関数名が存在しているかのチェックを行えばなんとかなるということに気がつきました。

ただし、本来デバッグ用のものですから、こういうことに用いていいのかどうかはわかりません(爆
※ 詳しい方コメントください。

function exclude_default_category_from_popular_list( $args, $taxonomies ) {
	if ( in_array( 'category', $taxonomies ) ) {
	$traces = debug_backtrace(); // 呼び出し元を取得する
		foreach ( $traces as $trace ) {
			// 関数名を照合
			if ( isset( $trace['function'] ) && $trace['function'] == 'wp_popular_terms_checklist' ) {
				// get_terms の引数に、デフォルトカテゴリーの除外を追加
				$args['exclude'] = array( get_option( 'default_category' ) );
				// 合致したらループをその場で抜ける
				break;
			}
		}
	}
	return $args;
}
add_filter( 'get_terms_args', 'exclude_default_category_from_popular_list', 10, 2 );