【WordPress】メタボックスの位置を全ユーザー統一する

カスタムフィールドを追加するプラグインなどを使用して、記事の編集画面の使い勝手をよくしていくと、編集画面にメタボックスがたくさん並びます。

メタボックスはドラッグアンドドロップでユーザーごとに好きな位置に動かせるのですが、これがお仕事のサイトだったりすると、マニュアル作成時に問題になったりします。

「マニュアルと同じところに入力欄がないんだけど!!」
しかも、表示位置の設定情報は簡単にクリアすることができません。

ということで、このユーザーごとの設定を効かせないようにすることはできないかなーと、コアファイルを覗いてみることにしました。

メタボックスを出力しているところは?

メタボックスを出力しているコードを特定するために、まずは編集画面のメタボックスを囲っているHTMLコードで、wp-adminディレクトリ内をgrepしてみます。

[kusanagi@minkapi wp-admin]$ grep -irn 'id="postbox-container-2"' * 
edit-form-advanced.php:699:<div id="postbox-container-2" class="postbox-container"> 
edit-form-comment.php:174:<div id="postbox-container-2" class="postbox-container"> 
edit-link-form.php:133:<div id="postbox-container-2" class="postbox-container"> 
includes/dashboard.php:220: <div id="postbox-container-2" class="postbox-container">

wp-admin/edit-form-advanced.phpの699行目に記載されているようなので、実際に中を見てみます。

<div id="postbox-container-2" class="postbox-container">
<?php

do_meta_boxes(null, 'normal', $post);

if ( 'page' == $post_type ) {
    /**
     * Fires after 'normal' context meta boxes have been output for the 'page' post type.
     *
     * @since 1.5.0
     *
     * @param WP_Post $post Post object.
     */
    do_action( 'edit_page_form', $post );
}
else {
    /**
     * Fires after 'normal' context meta boxes have been output for all post types other than 'page'.
     *
     * @since 1.5.0
     *
     * @param WP_Post $post Post object.
     */
    do_action( 'edit_form_advanced', $post );
}


do_meta_boxes(null, 'advanced', $post);

?>
</div>

do_meta_boxes()で出力しているみたい。
ということで、次はdo_meta_boxes()でgrep。

[kusanagi@minkapi wp-admin]$ grep -irn 'function do_meta_boxes' *
includes/template.php:995:function do_meta_boxes( $screen, $context, $object ) {

wp-admin/includes/template.phpの995行目の、do_meta_boxes()を見てみると…

function do_meta_boxes( $screen, $context, $object ) {
    global $wp_meta_boxes;
    static $already_sorted = false;

    if ( empty( $screen ) )
        $screen = get_current_screen();
    elseif ( is_string( $screen ) )
        $screen = convert_to_screen( $screen );

    $page = $screen->id;

    $hidden = get_hidden_meta_boxes( $screen );

    printf('<div id="%s-sortables" class="meta-box-sortables">', htmlspecialchars($context));

    // Grab the ones the user has manually sorted. Pull them out of their previous context/priority and into the one the user chose
    if ( ! $already_sorted && $sorted = get_user_option( "meta-box-order_$page" ) ) {
        foreach ( $sorted as $box_context => $ids ) {
            foreach ( explode( ',', $ids ) as $id ) {
                if ( $id && 'dashboard_browser_nag' !== $id ) {
                    add_meta_box( $id, null, null, $screen, $box_context, 'sorted' );
                }
            }
        }
    }

    $already_sorted = true;

    $i = 0;

    if ( isset( $wp_meta_boxes[ $page ][ $context ] ) ) {
        foreach ( array( 'high', 'sorted', 'core', 'default', 'low' ) as $priority ) {
            if ( isset( $wp_meta_boxes[ $page ][ $context ][ $priority ]) ) {
                foreach ( (array) $wp_meta_boxes[ $page ][ $context ][ $priority ] as $box ) {
                    if ( false == $box || ! $box['title'] )
                        continue;
                    $i++;
                    $hidden_class = in_array($box['id'], $hidden) ? ' hide-if-js' : '';
                    echo '<div id="' . $box['id'] . '" class="postbox ' . postbox_classes($box['id'], $page) . $hidden_class . '" ' . '>' . "\n";
                    if ( 'dashboard_browser_nag' != $box['id'] ) {
                        $widget_title = $box[ 'title' ];

                        if ( is_array( $box[ 'args' ] ) && isset( $box[ 'args' ][ '__widget_basename' ] ) ) {
                            $widget_title = $box[ 'args' ][ '__widget_basename' ];
                            // Do not pass this parameter to the user callback function.
                            unset( $box[ 'args' ][ '__widget_basename' ] );
                        }

                        echo '<button type="button" class="handlediv" aria-expanded="true">';
                        echo '<span class="screen-reader-text">' . sprintf( __( 'Toggle panel: %s' ), $widget_title ) . '</span>';
                        echo '<span class="toggle-indicator" aria-hidden="true"></span>';
                        echo '</button>';
                    }
                    echo "<h2 class='hndle'><span>{$box['title']}</span></h2>\n";
                    echo '<div class="inside">' . "\n";
                    call_user_func($box['callback'], $object, $box);
                    echo "</div>\n";
                    echo "</div>\n";
                }
            }
        }
    }

    echo "</div>";

    return $i;

}

1011行目から1019行目の間でソートしてる!

ソートを停止させよう

1011行目の、

if ( ! $already_sorted && $sorted = get_user_option( "meta-box-order_$page" ) ) {

の判定をtrueにすればいいのだけど、$already_sortedはfalseにするフックやらなんやらがないご様子。
ということで、get_user_option( “meta-box-order_$page” )をfalseにすることに。
get_user_option()をgrepして探してみます。

[kusanagi@minkapi wp-includes]$ grep -irn 'function get_user_option' *
user.php:451:function get_user_option( $option, $user = 0, $deprecated = '' ) {

wp-includes/user.phpの451行目を確認してみると…

function get_user_option( $option, $user = 0, $deprecated = '' ) {
    global $wpdb;

    if ( !empty( $deprecated ) )
        _deprecated_argument( __FUNCTION__, '3.0.0' )

    if ( empty( $user ) )
        $user = get_current_user_id();

    if ( ! $user = get_userdata( $user ) )
        return false;

    $prefix = $wpdb->get_blog_prefix();
    if ( $user->has_prop( $prefix . $option ) ) // Blog specific
        $result = $user->get( $prefix . $option );
    elseif ( $user->has_prop( $option ) ) // User specific and cross-blog
        $result = $user->get( $option );
    else
        $result = false;

    /**
     * Filters a specific user option value.
     *
     * The dynamic portion of the hook name, `$option`, refers to the user option name.
     *
     * @since 2.5.0
     *
     * @param mixed   $result Value for the user's option.
     * @param string  $option Name of the option being retrieved.
     * @param WP_User $user   WP_User object of the user whose option is being retrieved.
     */
    return apply_filters( "get_user_option_{$option}", $result, $option, $user );
}

apply_filters()を発見!!これでfalseにできるよー
※実際は$screenとかも遡って見てたりしましたが、もう$optionとかをvar_dump()してみちゃうほうが早い。

カスタムフィールドのユーザーごとの並び順設定を読み込まないようにした

下のコードで読み込みが停止できます。投稿タイプごとに設定が必要なので注意。

// カスタムフィールドのユーザーごとの並び順設定を読み込まないようにする 
add_filter( 'get_user_option_meta-box-order_post', '__return_false' ); // 投稿 
add_filter( 'get_user_option_meta-box-order_page', '__return_false' ); // 固定ページ 
add_filter( 'get_user_option_meta-box-order_[post_type]', '__return_false' ); // カスタム投稿