チームメンバー用のカスタム投稿タイプのテーマを作成しています。また、次のページ構造もあります。
about <-- this is a page
about/team-members <-- this is a page, lists all the team members
about/team-members/joe-bloggs <-- this is a custom post type (team member) entry
ここでの3番目の構造はaboutとteam memberのページを使いますが、それは親がteam memberとaboutであるように見せるためにカスタム投稿タイプのスラッグを使い続けます。カスタム投稿タイプに次のオプションを設定することでこれを達成しました。
...
'rewrite' => array( 'slug' => 'about/team-members', 'with_front' => false)
...
これはとてもうまくいきますが、チームメンバーの投稿レベルに到達すると、現在のページ、現在の先祖クラスが親ページに表示されなくなります。私たちは技術的にはそれらのページの親であるページではないので、これがなぜなのか知っています。
チームメンバー用のページを使用してこれをうまく実現できましたが、管理者が使いやすいようにカスタムの投稿タイプが代わりに選択されました。
みんなありがとう+女の子!
ページを操作するときは、親ページを選択できます。その値は、データベース内の子ページのpost_parent
フィールドの親ページID番号として保存されます。
あなたの場合、あなたはカスタム投稿タイプを使っているので、あなたはあなた自身のメタボックスを親ページのために作成する必要があるでしょう。何かのようなもの:
/* Define the custom box */
add_action('add_meta_boxes', 'child_cpt_add_custom_box');
/* Adds a box to the main column on the custom post type edit screens */
function child_cpt_add_custom_box() {
add_meta_box('child_cpt', __( 'My child_cpt parent'),'team_member_inner_custom_box','team_member');
}
/* Prints the box content */
function team_member_inner_custom_box() {
global $post;
// Use nonce for verification
wp_nonce_field( plugin_basename(__FILE__), 'team_member_inner_custom_box' );
echo 'Select the parent page';
$mypages = get_pages();
echo '<select name="cpt_parent">';
foreach($mypages as $page){
echo '<option value="'.$page->ID.'"';
if ($page->ID == $post->post_parent) {echo ' selected';}
echo '>"'.$page->post_title.'</option>';
}
echo '</select>';
}
/* Do something with the data entered */
add_action('wp_insert_post_data', 'myplugin_save_postdata');
/* When the post is saved, saves our custom data */
function myplugin_save_postdata( $data, $postarr ) {
global $post;
// verify this came from the our screen and with proper authorization,
// because save_post can be triggered at other times
if ( !wp_verify_nonce( $_POST['team_member_inner_custom_box'], plugin_basename(__FILE__) ) )
return $data;
// verify if this is an auto save routine.
// If it is our form has not been submitted, so we dont want to do anything
if ( defined('DOING_AUTOSAVE') && DOING_AUTOSAVE )
return $data;
// OK, we're authenticated: we need to find and save the data
if ($post->post_type == "team_member")
$data['post_parent'] = $_POST['cpt_parent'];
return $data;
}
register_post_type
とは関係ありません。あなたはWordPressを、それが別の投稿タイプ(ページ)の子ページであると考えるように騙しています。
免責事項: 試してみた後、これは私にはもはや存在しない問題と思われます。なぜなら、少なくとも私にとっては、それは私のWP 3.9.2インストールでしか動作しないためです。それに応じたバグトラッカーが見つかりませんでした。
私はこれをテストするための小さなプラグインをまとめました。これは誰かに役立つかもしれません。しかし、上記の免責事項で述べたように、私は現在のワードプレスのインストールでは問題を再現できませんでした。私は4つのファイルにプラグインを分けました、それらはプラグインディレクトリの中の1つのディレクトリに一緒になっています。
plugin-cpt_menu_hierarchy.php :
<?php
defined( 'ABSPATH' ) OR exit;
/**
* Plugin Name: CPT Menu Hierarchy Fix?
* Description: CPT Menu Hierarchy Fix?
* Author: ialocin
* Author URL: http://wordpress.stackexchange.com/users/22534/ialocin
* Plugin URL: http://wordpress.stackexchange.com/q/13308/22534
*/
// registering nonsense post type
include 'include-register_post_type.php';
// adding meta box to nosense custom post type
include 'include-cpt_parent_meta_box.php';
// menu highlighting fix
include 'include-menu_highlighting.php';
include-register_post_type.php :
<?php
defined( 'ABSPATH' ) OR exit;
// See: http://codex.wordpress.org/Function_Reference/register_post_type
add_action( 'init', 'wpse13308_basic_reigister_post_type');
function wpse13308_basic_reigister_post_type() {
$args = array(
'public' => true,
'label' => 'Nonsense'
);
register_post_type( 'nonsense', $args );
}
include-cpt_parent_meta_box.php :
<?php
defined( 'ABSPATH' ) OR exit;
// pretty much like @bainternet's answer
// Add Meta Box
add_action( 'add_meta_boxes', 'nonsense_add_meta_box' );
function nonsense_add_meta_box() {
add_meta_box(
'nonsense',
__( 'Nonsense parent' ),
'nonsense_inner_meta_box',
'nonsense'
);
}
// Meta Box Content
function nonsense_inner_meta_box() {
global $post;
wp_nonce_field(
plugin_basename( __FILE__ ),
'nonsense_inner_meta_box'
);
echo 'Parent Page: ';
$mypages = get_pages();
echo '<select name="cpt_parent">';
foreach($mypages as $page){
echo '<option value="'.$page->ID.'"';
if ($page->ID == $post->post_parent) {echo ' selected';}
echo '>'.$page->post_title.'</option>';
}
echo '</select>';
}
// Save Data From Meta Box
add_action( 'wp_insert_post_data', 'nonsense_save_meta_box_data' );
function nonsense_save_meta_box_data( $data, $postarr ) {
global $post;
if (
! wp_verify_nonce(
$_POST['nonsense_inner_meta_box'],
plugin_basename( __FILE__ )
)
) {
return $data;
}
if (
defined('DOING_AUTOSAVE')
&& DOING_AUTOSAVE
) {
return $data;
}
if ( $post->post_type == 'nonsense' ) {
$data['post_parent'] = $_POST['cpt_parent'];
}
return $data;
}
include-menu_highlighting.php :
<?php
defined( 'ABSPATH' ) OR exit;
// altering WordPress' nav menu classes via »nav_menu_css_class« filter
add_filter( 'nav_menu_css_class', 'wpse13308_fix_nav_menu_highlighting', 10, 2 );
function wpse13308_fix_nav_menu_highlighting( $classes, $item ) {
// data of the current post
global $post;
// setting up some data from the current post
$current_post_post_type = $post->post_type;
$current_post_parent_id = $post->post_parent;
// id of the post the current menu item represents
$current_menu_item_id = $item->object_id;
// do this for a certain post type
if( $current_post_post_type == 'nonsense' ) {
// remove unwanted highlighting class via array_filter and callback
// http://php.net/manual/de/function.array-filter.php
$classes = array_filter(
$classes,
'wpse13308_remove_highlighting_classes'
);
// when the parents id equals the menu items id, we want to
// highlight the parent menu item, so we check for:
if( $current_post_parent_id == $current_menu_item_id ) {
// use the css class used for highlighting
$classes[] = 'replace-with-css-class';
}
}
return $classes;
}
// callback to remove highlighting classes
function wpse13308_remove_highlighting_classes( $class ) {
return
(
// use the class(es) you need, overview over nav menu item css classes:
// http://codex.wordpress.org/Function_Reference/wp_nav_menu#Menu_Item_CSS_Classes
$class == 'highlight-class'
// uncomment next line if you want to check for more then one class
// repeat the line if you want to check for a third, fourth and so on
// || $class == 'replace-with-css-class'
)
? false
: true
;
}
私はもう少し時間をかけて自分自身を掘り下げる時間がありました(私が誰かの時間を無駄にした場合は申し訳ありません)。強調表示の問題を解決する最善の方法は、ちょっとやり直すことになるでしょう _wp_menu_item_classes_by_context()
やっている、それは私のカスタム投稿タイプの親として機能するメニュー項目のすべての親と祖先に対して反復し、適切にクラスを追加することです。
カスタム投稿タイプの親ページも修正し、親が変更されたらすべての投稿を更新せずに簡単に変更できるようにしたいので、カスタム投稿タイプの投稿のpost_parent
フィールドに入力する代わりにオプションを使用します。 。私はとにかく私のテーマでそれを使っているので私はそれのためにACFを使いました、しかしデフォルトのWordPressオプション機能を使うことはもちろんそれをやるでしょう。
私のニーズに合わせて、 wp_nav_menu_objects
フィルターを使用することができます。さらに、 page_for_posts
オプションをフィルタリングする を使用して、誤った/空の値を返すようにしたため、デフォルトの投稿ページも強調表示されません。
私はずっと行ったわけではないことに注意してください。フィルタはcurrent-menu-ancestor
クラスとcurrent-menu-parent
クラスを追加するだけです。これで十分です。
/**
* Filters the `page_for_posts` option on specific custom post types in
* order to avoid the wrong menu item being marked as
* `current-page-parent`.
*
* @see _wp_menu_item_classes_by_context()
*/
function wpse13308_pre_option_page_for_posts_filter()
{
$types = array
(
'my_custom_post_type_x',
'my_custom_post_type_y',
'my_custom_post_type_z'
);
if(in_array(get_post_type(), $types))
{
return 0;
}
return false;
}
add_filter('pre_option_page_for_posts', 'wpse13308_pre_option_page_for_posts_filter');
/**
* Returns the current posts parent page ID
*
* @return int
*/
function wpse13308_get_parent_page_id()
{
$postType = get_post_type();
$parentPageId = null;
switch($postType)
{
case 'my_custom_post_type_x':
case 'my_custom_post_type_y':
case 'my_custom_post_type_z':
$parentPageId = (int)get_field('page_for_' . $postType, 'options')->ID;
break;
case 'post':
$parentPageId = (int)get_option('page_for_posts');
break;
}
return $parentPageId;
}
/**
* Adds proper context based classes so that the parent menu items are
* being highlighted properly for custom post types and regular posts.
*
* @param array $menuItems
* @return array
*
* @see _wp_menu_item_classes_by_context()
*/
function wpse13308_wp_nav_menu_objects_filter(array $menuItems)
{
$parentPageId = wpse13308_get_parent_page_id();
if($parentPageId !== null)
{
$activeAncestorItemIds = array();
$activeParentItemIds = array();
foreach($menuItems as $menuItem)
{
if((int)$parentPageId === (int)$menuItem->object_id)
{
$ancestorId = (int)$menuItem->db_id;
while
(
($ancestorId = (int)get_post_meta($ancestorId, '_menu_item_menu_item_parent', true)) &&
!in_array($ancestorId, $activeAncestorItemIds)
)
{
$activeAncestorItemIds[] = $ancestorId;
}
$activeParentItemIds[] = (int)$menuItem->db_id;
}
}
$activeAncestorItemIds = array_filter(array_unique($activeAncestorItemIds));
$activeParentItemIds = array_filter(array_unique($activeParentItemIds));
foreach($menuItems as $key => $menuItem)
{
$classes = $menuItems[$key]->classes;
if(in_array(intval($menuItem->db_id), $activeAncestorItemIds))
{
$classes[] = 'current-menu-ancestor';
$menuItems[$key]->current_item_ancestor = true;
}
if(in_array($menuItem->db_id, $activeParentItemIds))
{
$classes[] = 'current-menu-parent';
$menuItems[$key]->current_item_parent = true;
}
$menuItems[$key]->classes = array_unique($classes);
}
}
return $menuItems;
}
add_filter('wp_nav_menu_objects', 'wpse13308_wp_nav_menu_objects_filter');
完全を期すために、オプションを使用する代わりにpost_parent
( @ Bainternet's answer /を参照)を入力するとき、親IDを取得すると次のようになります。
/**
* Returns the current posts parent page ID
*
* @return int
*/
function wpse13308_get_parent_page_id()
{
$parentPageId = null;
$post = get_post();
switch($post->post_type)
{
case 'my_custom_post_type_x':
case 'my_custom_post_type_y':
case 'my_custom_post_type_z':
$parentPageId = (int)$post->post_parent;
break;
case 'post':
$parentPageId = (int)get_option('page_for_posts');
break;
}
return $parentPageId;
}
私はカスタムウォーカーを使って似たようなことを実現しました。
class Walker_Page_CustomPostTypeHack extends Walker_Page {
function walk($elements, $max_depth) {
$called_with = func_get_args();
// current page is arg 3... see walk_page_tree for why
$current_page = $called_with[3];
// if there's no parent - see if we can find one.
// some ACF options would be an easy way to make this configurable instad of constants
if ($current_page === 0) {
global $wp_query;
$current_post = $wp_query->get_queried_object();
switch ($current_post->post_type) {
case 'course':
$current_page = POST_COURSES;
break;
case 'project':
$current_page = POST_PROJECTS;
break;
case 'story':
$current_page = POST_STORIES;
break;
}
}
// now pass on into parent
$called_with[3] = $current_page;
return call_user_func_array(array('parent', 'walk'), $called_with);
}
}
考えられる解決策は、カスタム投稿タイプを保存するときはいつでも、その親をプログラム的にabout/team-members
に設定することができます。
手順は次のとおりです。