it-swarm.com.de

Integrieren eines benutzerdefinierten Beitragstyps in eine Seitenhierarchie

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, es wird jedoch weiterhin der benutzerdefinierte Post-Typ "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, weil wir technisch gesehen nicht auf einem pagea Elternteil dieser Seiten sind. Gibt es jedoch eine Möglichkeit, wie ich tricksen/reparieren/bodge kann, damit die Seiten 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!

14
Ben Everard

Wenn Sie mit Seiten arbeiten, können Sie eine übergeordnete Seite auswählen, und dieser Wert wird als übergeordnete Seiten-ID im Feld post_parent 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. so 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 mit register_post_type zu tun. Sie täuschen WordPress vor, es handele sich um eine untergeordnete Seite eines anderen Beitragstyps (Seite).

6
Bainternet

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. Es konnte jedoch kein entsprechender Bug-Tracker gefunden werden.


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.
0
Nicolai

Ich hatte noch etwas Zeit, mich damit zu beschäftigen (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, was _wp_menu_item_classes_by_context() wird ausgeführt, dh es werden alle Eltern und Vorfahren des Menüelements durchlaufen, das als Eltern meines benutzerdefinierten Beitragstyps fungiert, und Klassen werden entsprechend hinzugefügt.

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 das Feld post_parent 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 könnte ich den Filter wp_nav_menu_objects verwenden. Außerdem musste ich filter the page_for_posts option , damit es einen fälschlichen/leeren Wert zurückgibt, vermeiden, dass die Standardpostseite ebenfalls hervorgehoben wird.

Beachten Sie, dass ich nicht den ganzen Weg gegangen bin, der Filter fügt nur die Klassen current-menu-ancestor und current-menu-parent hinzu, 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 beim Ausfüllen von post_parent (siehe @ Bainternets Antwort ) anstelle der Verwendung von Optionen das Abrufen der übergeordneten ID folgendermaßen 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;
}
0
ndm

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

}
0
benlumley

Eine mögliche Lösung besteht darin, dass Sie beim Speichern des benutzerdefinierten Beitragstyps dessen übergeordnetes Element auf about/team-members programmgesteuert festlegen können.

Hier sind die Schritte:

  1. Sie können den save_post-Haken 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 (ich habe es nicht selbst versucht, aber ich sehe nicht, warum es nicht funktionieren sollte).
0
Shahar Dekel