3.6 から使える(であろう)ちょっと便利な WordPress 関数

公開されている 3.6 ベータ3の中から、プラグインやテーマをを作るときに使えそうな WordPress 関数を7個ほど拾い集めてみました。詳細は、リンク先の CODEX を見てもらうとして(まだページしかない関数もありますが)、ざっくりと機能とフックの有無などを紹介しておきます。

shortcode_exists

パラメーターで指定されたショートコードタグが定義されているか判別する。

フック

なし

記述例

<?php if ( shortcode_exists( 'my_shortcode' ) ) : ?>

my_shortcode というショートコードが定義されているか判定する。

has_shortcode

パラメーターで指定されたショートコードタグが、検索対象文字列に存在するかどうか判定する。

フック

なし

記述例

<?php if ( has_shortcode( $post->post_content, 'trust-form' ) ) : ?>

本文内に Trust Form のショートコードが存在するか判別する。

get_content_images

パラメーターで指定された文字列の中に含まれる画像を全て(上限指定がある場合は指定数まで)取得する。

フック

フック名 フックの種類 パラメーター 備考
content_images フィルター 1.画像のURLの配列
2.検索対象文字列
フックを通るのは、取得形式を画像のURLと指定したときのみ

記述例

$content_images = get_content_images( $post->post_content, false, false, 3 );

本文内に含まれる最初の3つの画像のURLを取得する。

get_content_image

検索対象文字列の最初の画像を取得する

フック

フック名 フックの種類 パラメーター 備考
content_image フィルター 1.画像のタグまたはURL
2.検索対象文字列
画像のタグが見つからなかった場合は、フックを通らない

記述例

$content_images = get_content_image( $post->post_content );

本文内に含まれる最初のimgタグを取得する。

attachment_url_to_postid

画像ファイルのURLから、メディアのIDを取得する。ただし、指定できるURLはフルサイズのものに限られる

フック

なし

記述例

$attachment_id = attachment_url_to_postid( $src );

$srcのメディアIDを取得する。

wp_slash

対象データに対して再帰的に addslashes をかける

フック

なし

記述例

$datas = wp_slash( $datas );

$datas に再帰的に addslashes をかける

wp_unslash

対象データから、再帰的に stripcslashes によるバックスラッシュを除去を行う。既存のstripslashes_deepのラッパー関数

フック

なし

記述例

$post_data = wp_unslash( $_POST );

$_POST の値から、バックスラッシュを取り除く。

WordPress の管理画面にユーザー権限グループに応じた class を出力する

管理画面の body_class に相当する admin_body_class にフィルターフックをかけて、ユーザーの権限グループを含める方法

出力されるソースの修正ができない場合、これを使ってCSSで非表示にすることができますね。
CSSの読み込みは、admin_initなどで、wp_enqueue_style すればオッケー。

admin_body_class で渡されてくるものは文字列形式なので、扱いがちょっとめんどくさいのだけど。

function add_user_role_class( $admin_body_class ) {
	global $current_user;
	if ( ! $admin_body_class ) {
		$admin_body_class .= ' ';
	}
	$admin_body_class .= 'role-' . urlencode( $current_user->roles[0] );
	return $admin_body_class;
}
add_filter( 'admin_body_class', 'add_user_role_class' );

出力結果はこんな感じ

<body class="wp-admin wp-core-ui no-js  role-administrator index-php auto-fold admin-bar branch-3-5 version-3-5 admin-color-fresh locale-ja no-customize-support">

role-administrator で判別可能ですね。

非表示にする以外にもいろんな使い方ができそうな気がするので、こんな方法あるよってーのがあったら こそっと 教えてくださいw

カスタム投稿タイプを管理する Custom Post Type Maker を試してみた

管理画面からカスタム投稿タイプやら、カスタム分類やらを設定するために有名なプラグインといえば、Custom Post Type UI だと思いますが、デバッグモードにすると notice エラーが出たり(あまり人のこと言えない。。)、アップデートもしばらくされておらず、3.5 で追加になったパラメーターの指定ができなかったり、UIが今ひとつ使いにくいなど、今後にちょっと心配があり、代替となるプラグインで良いものないかなと探していたところ、Custom Post Type Maker の存在を知り、試してみました。

結論から言うと、現バージョンでは、使用すべきではないというのが私の判断です。

カスタム投稿タイプなどを新しく追加した場合、表示されるURLが新たに加わりますが、パーマリンク使用時にこのURLを表示するためには、リライトルールというマッピングデータを更新してあげる必要があります。

Custom Post Type Maker は、Webページ表示時にこのリライトルールを毎回再生成しているのですね。こうすることによって、新しくカスタム投稿タイプやカスタム分類を追加したときにも、自動的に表示されるようになるのですが、このリライトルールの再生成は、それなりに重い処理で表示の際に毎回やるようなことではありません。これは、Codex の flush_rewrite_rules の説明にもしっかりと注意書きとして載っています。

Important: Flushing the rewrite rules is an expensive operation, there are tutorials and examples that suggest executing it on the ‘init’ hook. This is bad practice. Instead you should flush rewrite rules on the activation hook of a plugin, or when you know that the rewrite rules need to be changed ( e.g. the addition of a new taxonomy or post type in your code ).

WordPress は、3.3 で、リライトルールの改善を行い、この処理も大幅に軽減はされていますが、毎回行うようなことではありません。

私の判断ではありますが、この点が改善されるまで、利用すべきでないプラグインととらえています。

カスタム分類を利用して、先頭に固定表示を実現する

WordPress には、先頭に固定表示の機能もありますが、1ページ目の最初にモリッと差し込まれるので、表示件数が変わってしまったり、ページ送りにも対応できません。

これを、カスタム分類を用いて、

  • 該当分類に属するものを、home 表示の際に優先表示
  • 1ページの表示投稿数は、設定通りにする
  • 先頭固定表示の記事のあとは、それ以外の記事を最新順に
  • ページ送り・ページナビに影響がでないこと

を満たすものを作ってみました。

具体的には、news というカスタム分類で、sticky という分類に属する投稿を先頭表示されるようにしています。

また、些細な工夫として、複数箇所から呼ばれる get_custom_sticky_posts で、wp_cache_set と wp_cache_get を使っています。これを使うと、実行結果がメモリ上にキャッシュされるため、複数回呼ばれるような場面においても、パフォーマンスロスを防ぐことができるようになります。

/*
 * 通常表示されるべき投稿を先頭固定の投稿で差し替える
 */
function insert_custom_sticky_posts( $posts, $wp_query ) {
	if ( ! is_admin() && $wp_query->is_home() ) {
		$stickies = get_custom_sticky_posts();
		if ( $stickies ) {
			$posts_per_page = $wp_query->get( 'posts_per_page' );
			$paged = $wp_query->get( 'paged' ) ? $wp_query->get( 'paged' ) : 1;

			if ( count( $stickies ) >= $posts_per_page * $paged ) { // 全て差し替え
				$posts = array_slice( $stickies, $posts_per_page * ( $paged - 1 ), $posts_per_page );
			} elseif ( count( $stickies ) > $posts_per_page * ( $paged - 1 ) ) {
				$insert_sickies = array_slice( $stickies, $posts_per_page * ( $paged - 1 ), $posts_per_page );
				$posts = array_merge( $insert_sickies, $posts );
				$posts = array_slice( $posts, 0, $posts_per_page );
			}
		}
	}
	return $posts;
}
add_filter( 'the_posts', 'insert_custom_sticky_posts', 10, 2 );

/*
 * 全投稿数に先頭固定の投稿数を足す
 */
function add_custom_sticky_posts_count( $found_posts, $wp_query ) {
	if ( ! is_admin() && $wp_query->is_home() && ! $wp_query->get( 'suppress_filters' ) ) {
		$found_posts = $found_posts + count( get_custom_sticky_posts() );
	}

	return $found_posts;
}
add_filter( 'found_posts', 'add_custom_sticky_posts_count', 10, 2 );

/*
 * SQL文のLIMIT 句を改変し、先頭固定の投稿数文を差し引いた開始位置にする
 */
function replace_custom_sticky_posts_limit_sql( $limits, $wp_query ) {
	if ( ! is_admin() && $wp_query->is_home() ) {
		$stickies = get_custom_sticky_posts();
		if ( $stickies ) {
			$posts_per_page = $wp_query->get( 'posts_per_page' );
			$paged = $wp_query->get( 'paged' ) ? $wp_query->get( 'paged' ) : 1;

			$start = $posts_per_page * ( $paged - 1 ) - count( $stickies );
			if ( $start < 0 ) {
				$start = 0;
			}
			$limits = "LIMIT $start, $posts_per_page";
		}
	}

	return $limits;
}
add_filter( 'post_limits', 'replace_custom_sticky_posts_limit_sql', 10, 2 );

/*
 * 通常表示の記事から、先頭固定表示する投稿を除外する
 */
function ignore_custom_stickies( $wp_query ) {
	if ( ! is_admin() && $wp_query->is_home() && ! $wp_query->get( 'suppress_filters' ) ) {
		$wp_query->set(
			'tax_query',
			array(
				'relation' => 'AND',
				array(
					'taxonomy' => 'news',
					'terms' => array( 'sticky' ),
					'field' => 'slug',
					'operator' => 'NOT IN',
				),
			)
		);
	}
}
add_action( 'pre_get_posts' , 'ignore_custom_stickies' );

/*
 * 先頭固定表示する投稿を全て取得する
 */
function get_custom_sticky_posts() {
	$cache_key = 'custom-sticky-posts';
	$stickies = wp_cache_get( $cache_key );

	if ( $stickies === false ) {
		$stickies = get_posts(
			array(
				'posts_per_page' => -1,
				'tax_query' => array(
					'relation' => 'AND',
					array(
						'taxonomy' => 'news',
						'terms' => array( 'sticky' ),
						'field' => 'slug',
						'operator' => 'IN',
					),
				)
			)
		);
		wp_cache_set( $cache_key, $stickies );
	}
	return $stickies;
}

■ 更新
2013.03.22 suppress_filters による条件分岐を追加しました。

WordPress 3.5 からの新アップローダーでもメディア表示をアップロードしたユーザーのみに限定する

以前投稿した、WordPress のメディア表示をアップロードしたユーザーのみに限定する では、3.5から導入された新アップローダーのメディア表示には対応できていませんでした。これは、新アップローダーが Ajax で動作しているため、先の記事で紹介した条件分岐の is_main_query に合致せず、ユーザーでの限定条件が付加されなかったのが原因です。

新アップローダーの画像取得は、wp-admin/includes/ajax-actions.phpwp_ajax_query_attachments で行われています。
画像の取得自体は、WP_Query を利用しているので、旧バージョンで用いた pre_get_posts でのフックも有効ではありますが、残念な事に、このままだとこの関数によるクエリーかどうかの判別条件が曖昧です。

そこで、この Ajax が動作するときのみ有効となる定数を設定して、それを pre_get_posts の条件に加えることにします。

WordPress の Ajax は、wp-admin/admin-ajax.php にリクエストされ、アクションフックを用いて、実行関数が呼ばれるようになっています。該当箇所のコードは、

CODE 1

add_action( 'wp_ajax_' . $_GET['action'], 'wp_ajax_' . str_replace( '-', '_', $_GET['action'] ), 1 );

となっており、画像取得の場合は、wp_ajax_query-attachments というフックが呼ばれます。Ajax の実行関数が呼ばれる優先度は、CODE 1 を見てわかるように 1 となっているので、定数の定義を行うには、これより優先度が高い(数字が少ない)優先度を設定して、早く行わなければなりません。

優先度 0 にて、DOING_QUERY_ATTACHMENT という定数を設定し、この定数の定義の有無により、ユーザーの条件を付加させるためには、

CODE 2

/*
 * メディアの抽出条件にログインユーザーの絞り込み条件を追加する
 */
function display_only_self_uploaded_medias( $wp_query ) {
	if ( is_admin() && ( $wp_query->is_main_query() || ( defined( 'DOING_QUERY_ATTACHMENT' ) && DOING_QUERY_ATTACHMENT ) ) && $wp_query->get( 'post_type' ) == 'attachment' ) {
		$user = wp_get_current_user();
		$wp_query->set( 'author', $user->ID );
	}
}
add_action( 'pre_get_posts', 'display_only_self_uploaded_medias' );


function define_doing_query_attachment_const() {
	if ( ! defined( 'DOING_QUERY_ATTACHMENT' ) ) {
		define( 'DOING_QUERY_ATTACHMENT', true );
	}
}
add_action( 'wp_ajax_query-attachments', 'define_doing_query_attachment_const', 0 );

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