Integrieren eines benutzerdefinierten Beitragstyps in eine Seitenhierarchie

14

Ich erstelle ein Thema mit einem benutzerdefinierten Beitragstyp für Teammitglieder. Außerdem habe ich die folgende Seitenstruktur:

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

In der dritten Struktur werden die Seiten "Info" und "Teammitglied" verwendet. Anschließend wird jedoch der benutzerdefinierte Beitragstyp "Slug" verwendet, um den Eindruck zu erwecken, dass die Eltern Teammitglied und "Info" sind. Ich habe dies erreicht, indem ich die folgenden Optionen für den benutzerdefinierten Beitragstyp festgelegt habe:

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

Das funktioniert prima, aber wenn ich auf die Post-Ebene des Teammitglieds komme, erhalte ich nicht mehr die Klassen current-page und current-ancestor auf den übergeordneten Seiten. Ich weiß, warum das so ist, denn wir sind technisch gesehen keine pagea Eltern dieser Seiten, aber gibt es eine Möglichkeit, wie ich die Seiten austricksen / reparieren / bodge kann, damit sie als Eltern erscheinen?

Ich hatte dies sehr gut durch die Verwendung von Seiten für Teammitglieder erreicht, jedoch wurde stattdessen ein benutzerdefinierter Beitragstyp ausgewählt, um die Verwendung für den Administrator zu vereinfachen.

Danke Jungs + Mädels!

Ben Everard
quelle
Sie müssen die Seiten-ID der Teammitglieder als benutzerdefinierten Beitragstyp post_parent festlegen.
Bainternet
Ich sehe diese Option nicht in der register_post_typeDokumentation. Können Sie mir helfen?
Ben Everard

Antworten:

6

Wenn Sie mit Seiten arbeiten, können Sie eine übergeordnete Seite auswählen, und dieser Wert wird als übergeordnete Seiten-ID im post_parentFeld der untergeordneten Seite in der Datenbank gespeichert.

In Ihrem Fall verwenden Sie einen benutzerdefinierten Beitragstyp, sodass Sie eine eigene Metabox für die übergeordnete Seite erstellen müssen. etwas wie:

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

Es hat nichts damit zu tun register_post_type. Sie täuschen WordPress vor, es handele sich um eine untergeordnete Seite eines anderen Beitragstyps (Seite).

Bainternet
quelle
1
Richtig, so kann ich sehen, wie dieses "Narren" WordPress eine bestimmte Seite für übergeordnet hält, aber es fügt der übergeordneten Seite nicht die übergeordnete Klasse hinzu, wenn ich wp_list_pages.
Ben Everard
1
Ich habe bemerkt, dass dies auch mit meiner Slug / Permalink-Struktur
Ben Everard
2
Ich versuche das Gleiche wie Ben zu erreichen, aber ich benutze es wp_nav_menu- das Post_Parent handelt von / Team-Mitgliedern, aber die Navigation hebt das übergeordnete Element meiner "normalen" Blog-Posts hervor ... Gibt es eine andere Idee, wie ich das beheben könnte?
pkyeck
@BenEverard: Hast du eine Lösung für das Permalink-Struktur-Chaos gefunden?
abaumg
0

Ich bin mit einem benutzerdefinierten Walker gegangen, um etwas Ähnliches zu erreichen. Es werden keine benutzerdefinierten Felder benötigt, aber alle Posts eines Typs müssen sich unter demselben Punkt im Seitenbaum befinden.

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
quelle
0

Haftungsausschluss: Nachdem ich es ausprobiert habe, scheint mir dies ein nicht mehr existierendes Problem zu sein, da es - zumindest für mich - nur auf meiner WP 3.9.2-Installation funktioniert. Konnte jedoch keinen entsprechenden Bug-Tracker finden.


Ich habe zusammen ein kleines Plugin herausgebracht, um dies zu testen, was vielleicht jemandem helfen könnte. Aber wie ich oben schon sagte, konnte ich das Problem in einer aktuellen WordPress-Installation nicht reproduzieren. Ich habe das Plugin in vier Dateien aufgeteilt, die in einem Verzeichnis innerhalb des Plugin-Verzeichnisses zusammengefasst sind.

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



  • Dies ist ein etwas verallgemeinertes Codebeispiel.
  • Es muss an den tatsächlichen Anwendungsfall angepasst werden.
Nicolai
quelle
0

Eine mögliche Lösung ist, wann immer der benutzerdefinierte Beitragstyp gespeichert wird, Sie können den übergeordneten about/team-membersBeitragstyp so einstellen , dass er programmatisch ist.

Hier sind die Schritte:

  1. Sie können den Haken save_post verwenden, um zu "fangen", wann immer jemand versucht, einen Beitrag zu speichern.
  2. Wenn es sich bei diesem Beitrag um den benutzerdefinierten Beitragstyp handelt, nach dem Sie suchen, fahren Sie fort.
  3. Stellen Sie sicher, dass das übergeordnete Element des benutzerdefinierten Posts auf die gewünschte Seite eingestellt ist (Sie können die Seiten-ID fest codieren, solange Sie sie nicht löschen). Sie können wp_update_post verwenden , um das übergeordnete Element zu speichern (das habe ich selbst nicht versucht, aber ich verstehe nicht, warum es nicht funktionieren sollte).
Shahar Dekel
quelle
Ich würde sehr gerne Code dafür sehen! Das wäre perfekt, aber ich kann es nicht zum Laufen bringen.
Johan Dahl
0

Ich hatte noch etwas Zeit, mich selbst damit zu befassen (sorry, wenn ich jemandes Zeit verschwendet hätte), und ich dachte mir, dass der beste Weg, um das Hervorhebungsproblem zu lösen, darin besteht, etwas zu wiederholen _wp_menu_item_classes_by_context() , darin besteht, das, tue, ein , das ist über alles hinweg Eltern und Vorfahren des Menüelements, das als Eltern meines benutzerdefinierten Beitragstyps fungiert, und fügen Klassen entsprechend hinzu.

Da ich auch wollte, dass die übergeordnete Seite für meinen benutzerdefinierten Beitragstyp repariert und einfach geändert werden kann, ohne dass alle Beiträge aktualisiert werden müssen, sobald sich der übergeordnete ändert, habe ich beschlossen, eine Option zu verwenden, anstatt die zu füllen post_parent Feld meiner benutzerdefinierten Beiträge auszufüllen. Ich habe ACF dafür verwendet, da ich es sowieso in meinem Theme verwende, aber die standardmäßige Funktionalität der WordPress-Option würde es natürlich auch tun.

Für meine Bedürfnisse konnte ich den wp_nav_menu_objectsFilter nutzen. Außerdem musste ich die page_for_postsOption so filtern , dass sie einen falschen / leeren Wert zurückgibt. Dadurch wird vermieden, dass die Seite mit den Standardbeiträgen ebenfalls hervorgehoben wird.

Beachten Sie, dass ich nicht den ganzen Weg gegangen bin, der Filter fügt nur die Klassen current-menu-ancestorund hinzu current-menu-parent, da dies für meine Bedürfnisse ausreichte!

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

Der Vollständigkeit halber könnte das Abrufen der übergeordneten ID beim Auffüllenpost_parent (siehe @ Bainternets Antwort ) anstelle der Verwendung von Optionen ungefähr so aussehen:

/**
 * 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
quelle
Sie haben meine Zeit nicht verschwendet :) Eine andere Sache, sind Sie sicher, dass dies immer noch ein Problem ist? Weil ich es in meiner WP 3.9.2-Installation nicht reproduzieren konnte. Das Hervorheben des richtigen Menüpunkts hat sofort funktioniert.
Nicolai
Ja, es ist definitiv immer noch ein Problem @ialocin. Könnte es sein, dass Sie dies mit einem Menü der Stufe 0 und dem Standardposttyp testen?
ndm
Nein, habe es mit dem Code aus meiner Antwort versucht. Also mit einem benutzerdefinierten Beitragstyp und als 1. und 2. Ebene Menüpunkt zu einer Seite aus dem entsprechenden Beitragstyp. Ich habe die im WordPress-Kern enthaltenen Themes zum Testen verwendet.
Nicolai
@ialocin Nicht sicher, ob ich Sie richtig verstehe, weil " versucht mit dem Code geschrieben " und " out of the box " sich irgendwie gegenseitig ausschließen? ;) Beziehen Sie sich nur auf den benutzerdefinierten Beitragstyp, nicht auf die Hervorhebungskorrektur?
ndm
Richtig :) Ok, um genau zu sein, für das Szenario wird ein CPT benötigt, also habe ich es natürlich registriert. Das Hervorheben funktioniert ohne die Verwendung der Meta-Box und der Hervorhebungskorrektur. Zum Beispiel mit einer Menüstruktur: grandparent (page)> parent (page)> something (post)> another-thing (cpt)> one-more-thing (cpt) - jedes Element erhält die richtige CSS-Klasse (n); Thema verwendet hier dreizehnundzwanzig.
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

EDIT 1:

Da Zeiger nicht funktionieren:

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
quelle
Es geht los. Es wurde im Filter-Hook wp_nav_menu_objects hinzugefügt.
Aifrim