Интеграция пользовательского типа записи в иерархию страниц

14

Я создаю тему с пользовательским типом записи для членов команды, у меня также есть следующая структура страницы:

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

Третья структура здесь использует страницы about и team member, но далее использует настраиваемый слаг типа post, чтобы выглядело, как будто его родители являются членами команды и около. Я добился этого, установив следующие параметры для пользовательского типа сообщения:

...
'rewrite' => array( 'slug' => 'about/team-members', 'with_front' => false)
...

Это прекрасно работает, однако, когда я перехожу на уровень должности члена команды, я больше не получаю классы текущей страницы, текущего предка на родительских страницах. Я знаю, почему это так, потому что технически мы не являемся родителями этих страниц, но есть ли способ, которым я могу обмануть / исправить / упрекнуть, чтобы страницы действительно выглядели как родители?

Я добился этого красиво, используя страницы для членов команды, однако вместо этого был выбран собственный тип поста для простоты использования для администратора.

Спасибо, ребята + девочки!

Бен Эверард
источник
вам нужно установить идентификатор страницы членов команды в качестве вашего пользовательского типа поста post_parent.
Bainternet
Я не вижу этот вариант в register_post_typeдокументации, вы можете помочь?
Бен Эверард,

Ответы:

6

При работе со страницами вы можете выбрать родительскую страницу, и это значение сохраняется в качестве идентификатора родительской страницы в поле дочерней страницы post_parentв базе данных.

В вашем случае вы используете пользовательский тип записи, поэтому вам нужно будет создать собственный метабокс для родительской страницы; что-то вроде:

/* 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, думая, что это дочерняя страница другого типа (страница).

Bainternet
источник
1
Итак, я могу видеть, как этот «дурак» WordPress считает, что конкретная страница является его родительской, но при этом он не добавляет родительский класс страницы на родительскую страницу wp_list_pages.
Бен Эверард
1
Я заметил, что это также портит мою структуру slug / permalink ...: S
Бен Эверард,
2
я пытаюсь добиться того же, что и Бен, но я использую wp_nav_menu- post_parent о / team-members, но навигация выделяет родительский элемент моих "обычных" сообщений в блоге ... есть еще идеи, как мне это исправить?
pkyeck
@BenEverard: Вы нашли решение проблемы структуры постоянных ссылок?
abaumg
0

Я пошел с пользовательским обходчиком, чтобы достичь чего-то похожего ... избегая необходимости в настраиваемых полях, но все посты типа должны находиться ниже одной и той же точки в дереве страниц.

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);
    }

}
benlumley
источник
0

Отказ от ответственности: После того, как я попробовал, это кажется мне уже не существующей проблемой, потому что - по крайней мере для меня - он работает только на моей установке WP 3.9.2. Не удалось найти соответствующий трекер ошибок.


У меня есть вместе небольшой плагин для проверки этого, который может помочь кому-то. Но, как я уже сказал в приведенном выше отказе от ответственности, я не смог воспроизвести проблему в текущей установке WordPress. Я разделил плагин на четыре файла, они собираются вместе в один каталог внутри каталога плагина.

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:&nbsp;&nbsp;';
    $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
    ;
}



  • Это несколько обобщенный пример кода.
  • Это должно быть приспособлено к фактическому случаю использования.
Nicolai
источник
0

Возможное решение заключается в том, что всякий раз, когда пользовательский тип записи сохраняется, вы можете установить его «родительский» как about/team-membersпрограммный.

Вот шаги:

  1. Вы можете использовать хук save_post, чтобы «ловить» всякий раз, когда кто-то пытается сохранить сообщение.
  2. Если это сообщение является пользовательским типом публикации, к которому вы стремитесь, тогда продолжайте.
  3. Обязательно установите родительский элемент пользовательской записи на нужную страницу (вы можете жестко закодировать идентификатор страницы, если не удаляете ее). Вы можете использовать wp_update_post для сохранения родителя (я сам не пробовал, но не понимаю, почему это не должно работать).
Шахар Декель
источник
Я бы очень хотел увидеть код для этого! Это было бы прекрасно, но я не могу заставить его работать сам.
Йохан Даль
0

У меня было еще немного времени, чтобы покопаться в этом сам (извините, если я потратил чье-то время), и я подумал, что для меня лучший способ решить проблему выделения - это сделать что-то новое. _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 ) вместо использования опций получение родительского идентификатора может выглядеть примерно так:

/**
 * 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;
}
НДД
источник
Вы не потратили впустую мое время :) Другое дело, уверены, что это все еще проблема? Потому что на моей установке WP 3.9.2 я не мог воспроизвести его. Подсветка правильного пункта меню работала просто из коробки.
Николай
Да, это определенно все еще проблема @ialocin. Может быть, вы тестируете это с меню уровня 0 и типом сообщения по умолчанию?
РПС
Нет, попробовал с кодом, размещенным в моем ответе. То есть с пользовательским типом записи и как пункт меню 1-го и 2-го уровня на страницу из соответствующего типа сообщения. Я использовал темы WordPress для проверки.
Николай
@ialocin Не уверен, правильно ли я вас понимаю, потому что " пробовал с опубликованным кодом " и " из коробки " вроде как взаимоисключающие? ;) Вы имеете в виду только пользовательский тип сообщения, а не исправление выделения?
РПС
Правильно :) Хорошо, если быть точным, для сценария необходим CPT, поэтому, конечно, я зарегистрировал его. Подсветка работает без использования мета-блока и исправления подсветки. Например, со структурой меню: grandparent (page)> parent (page)> что-то (post)> another-thing (cpt)> one-more-thing (cpt) - каждый элемент получает правильный класс (ы) css; Тема использовалась здесь двадцать тринадцать.
Николай
-1
<?php
the_post();

// $postType holds all the information of the post type of the current post you are viewing
$postType = get_post_type_object(get_post_type());

// $postSlug is the slug you defined in the rewrite column: about/team-members
$postSlug = $postType->rewrite['slug'];

// $datas = { [0] => 'about', [1] => 'team-members' }
$datas = explode('/', $postSlug);

// $pageSlug = 'about'
$pageSlug = $datas[0];

// all the page information you require.
$page = get_page_by_path($pageSlug, OBJECT, 'page');
?>

http://codex.wordpress.org/Function_Reference/get_post_type_object http://codex.wordpress.org/Function_Reference/get_page_by_path

РЕДАКТИРОВАТЬ 1:

Так как указатели не работают:

add_filter('wp_nav_menu_objects', 'my_menu_class_edit');
function my_menu_class_edit($items)
{
    if (is_single()) {
        $postType = get_post_type_object(get_post_type());
        $postSlug = $postType->rewrite['slug'];
        if($postSlug  != 'about/team-members')
            return $items;
        $datas = explode('/', $postSlug);
        $pageAbout = get_page_by_path($datas[0], OBJECT, 'page');
        $pageTeamMembers = get_page_by_path($datas[1], OBJECT, 'page');

        foreach ($items as $item) {
            if ($item->title == $pageAbout->post_title) {
                $item->classes[] = 'current-ancestor';
            } else if ($item->title == $pageTeamMembers->post_title) {
                $item->classes[] = 'current-page';
            }
        }
   }
    return $items;
}
aifrim
источник
Вот и ты. Добавил его в ловушку фильтра wp_nav_menu_objects.
апреля