Integrowanie niestandardowego typu postu z hierarchią stron

14

Tworzę motyw z niestandardowym typem posta dla członków zespołu, mam również następującą strukturę strony:

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

Trzecia struktura tutaj używa stron o członkach i członkach zespołu, ale dalej używa niestandardowego ślimaka typu post, aby wyglądać tak, jakby jego rodzice byli członkami zespołu. Osiągnąłem to, ustawiając następujące opcje niestandardowego typu postu:

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

Działa to świetnie, jednak kiedy przejdę do poziomu posta członka zespołu, nie otrzymuję już klas bieżącej strony, bieżący przodek na stronach nadrzędnych. Wiem, dlaczego tak jest, ponieważ technicznie nie jesteśmy na stronie nadrzędnej dla tych stron, ale czy istnieje sposób, w jaki mogę oszukać / naprawić / zablokować strony, aby strony pojawiły się jako rodzice?

Udało mi się to dobrze, używając stron dla członków zespołu, jednak zamiast tego wybrano niestandardowy typ postu, aby administrator mógł go łatwo użyć.

Dzięki chłopaki + dziewczyny!

Ben Everard
źródło
musisz ustawić identyfikator strony członków zespołu jako niestandardowy typ posta post_parent.
Bainternet
Nie widzę tej opcji w register_post_typedokumentacji, możesz pomóc?
Ben Everard

Odpowiedzi:

6

Podczas pracy ze stronami możesz wybrać stronę nadrzędną, a ta wartość jest zapisywana jako numer identyfikacyjny strony nadrzędnej w polu strony podrzędnej post_parentw bazie danych.

W twoim przypadku używasz niestandardowego typu postu, więc musisz utworzyć własny metaboks dla strony nadrzędnej; coś jak:

/* 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;
}

To nie ma nic wspólnego register_post_type. Oszukujesz WordPressa, aby pomyślał, że jest to strona podrzędna innego typu posta (strony).

Bainternet
źródło
1
W porządku, więc mogę zobaczyć, jak ten „oszukuje” WordPressa, że ​​konkretna strona jest nadrzędna, ale nie dodaje klasy nadrzędnej strony do strony nadrzędnej, gdy ja wp_list_pages.
Ben Everard
1
Zauważyłem, że to też zadziera z moją strukturą ślimaka / permalinka ...: S
Ben Everard
2
staram się osiągnąć to samo, co Ben, ale używam wp_nav_menu- post_parent dotyczy / członków zespołu, ale nawigacja podkreśla element nadrzędny moich „normalnych” postów na blogu ... inny pomysł, jak to naprawić?
pkyeck 10.10.11
@BenEverard: Czy znalazłeś rozwiązanie dla bałaganu w strukturze permalink?
abaumg
0

Poszedłem z niestandardowym walkerem, aby osiągnąć coś podobnego ... unika potrzeby niestandardowych pól, ale wszystkie posty danego typu muszą znajdować się poniżej tego samego punktu w drzewie strony.

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
źródło
0

Oświadczenie: Po wypróbowaniu wydaje mi się to nieistniejącym problemem, ponieważ - przynajmniej dla mnie - działa tylko na mojej instalacji WP 3.9.2. Nie udało się znaleźć odpowiedniego narzędzia do śledzenia błędów.


Mam razem małą wtyczkę, aby to przetestować, co może komuś pomóc. Ale, jak powiedziałem w powyższym zrzeczeniu się odpowiedzialności, nie mogłem odtworzyć problemu w bieżącej instalacji wordpress. Rozdzieliłem wtyczkę na cztery pliki, idą one razem do jednego katalogu w katalogu wtyczek.

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



  • Jest to nieco uogólniony przykład kodu.
  • Musi być dopasowany do rzeczywistego przypadku użycia.
Nicolai
źródło
0

Możliwym rozwiązaniem jest to, że za każdym razem, gdy zapisywany jest niestandardowy typ postu, możesz ustawić jego about/team-memberselement nadrzędny na prgramatyczny.

Oto kroki:

  1. Możesz użyć haka save_post, aby „złapać” za każdym razem, gdy ktoś próbuje zapisać post.
  2. Jeśli ten post jest niestandardowym typem, którego szukasz, kontynuuj.
  3. Pamiętaj, aby ustawić nadrzędnego niestandardowego posta na żądaną stronę (możesz na stałe zakodować identyfikator strony, o ile go nie usuniesz). Możesz użyć wp_update_post, aby zapisać rodzica (sam tego nie próbowałem, ale nie rozumiem, dlaczego to nie powinno działać).
Shahar Dekel
źródło
Bardzo chciałbym zobaczyć kod do tego! Byłoby to idealne, ale nie jestem w stanie sprawić, by działał sam.
Johan Dahl
0

Miałem więcej czasu, żeby się w to zagłębić (przepraszam, jeśli zmarnowałem czyjś czas) i pomyślałem, że dla mnie najlepszym sposobem rozwiązania problemu wyróżnienia byłoby ponowne zrobienie tego, co _wp_menu_item_classes_by_context() się dzieje, czyli powtórzenie wszystkiego rodzice i przodkowie elementu menu, który działa jako rodzic mojego niestandardowego typu postu i odpowiednio dodają klasy.

Ponieważ chciałem również naprawić stronę nadrzędną dla mojego niestandardowego typu postu i łatwo ją zmienić bez konieczności aktualizacji wszystkich postów po zmianie elementu nadrzędnego, postanowiłem użyć opcji zamiast zapełniać post_parent pola moich niestandardowych typów postów. Użyłem do tego ACF, ponieważ i tak używam go w moim motywie, ale użycie domyślnej funkcji opcji WordPress oczywiście by to zrobiło.

Na moje potrzeby mogłem skorzystać z wp_nav_menu_objectsfiltra. Dodatkowo musiałem odfiltrować page_for_postsopcję, aby zwróciła wartość false / empty, dzięki czemu unika się podświetlania domyślnej strony postów.

Pamiętaj, że nie poszedłem na całość, filtr dodaje tylko klasy current-menu-ancestori current-menu-parent, ponieważ to wystarczyło na moje potrzeby!

/**
 * 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');

Ze względu na kompletność, podczas wypełniania post_parent(patrz odpowiedź @ Bainternet ) zamiast korzystania z opcji, wówczas pobieranie identyfikatora nadrzędnego może wyglądać mniej więcej tak:

/**
 * 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;
}
ndm
źródło
Nie zmarnowałeś mojego czasu :) Kolejna sprawa, czy na pewno to problem? Ponieważ w mojej instalacji WP 3.9.2 nie mogłem go odtworzyć. Podświetlanie prawidłowego elementu menu działało zaraz po wyjęciu z pudełka.
Nicolai
Tak, to zdecydowanie problem @ialocin. Czy to możliwe, że testujesz to z menu poziomu 0 i domyślnym typem postu?
ndm
Nie, próbowałem z kodem opublikowanym w mojej odpowiedzi. Tak więc z niestandardowym typem postu oraz jako pozycja menu 1. i 2. poziomu na stronie z odpowiedniego typu posta. Użyłem podstawowych pakietów Wordpress do przetestowania go.
Nicolai
@ialocin Nie jestem pewien, czy dobrze cię rozumiem, ponieważ „ wypróbowałem z opublikowanym kodem ” i „po wyjęciu z pudełka ” wzajemnie się wykluczają? ;) Czy odnosisz się tylko do niestandardowego typu postu, a nie do podświetlania?
ndm
Racja :) Ok, konkretnie, w scenariuszu potrzebny jest CPT, więc oczywiście go zarejestrowałem. Podświetlanie działa bez użycia pola meta i poprawki podświetlania. Na przykład ze strukturą menu: dziadek (strona)> rodzic (strona)> coś (post)> inna rzecz (cpt)> jeszcze jedna rzecz (cpt) - każdy element otrzymuje poprawną klasę (-y) css; motyw użyty tutaj dwadzieścia trzynaście.
Nicolai
-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

EDYCJA 1:

Ponieważ wskaźniki nie działają:

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
źródło
Proszę bardzo. Dodano go w ramach haka filtru wp_nav_menu_objects.
aifrim