Integrazione di un tipo di post personalizzato in una gerarchia di pagine


14

Sto creando un tema con un tipo di post personalizzato per i membri del team, ho anche la seguente struttura di pagina:

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

La terza struttura utilizza le pagine about e dei membri del team, ma continua a utilizzare la lumaca di tipo post personalizzato per far sembrare che i genitori siano membri del team e circa. Ho raggiunto questo obiettivo impostando le seguenti opzioni sul tipo di post personalizzato:

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

Funziona benissimo, tuttavia quando arrivo al livello di post dei membri del team non ottengo più le classi della pagina corrente e degli antenati correnti nelle pagine padre. So perché, perché non siamo tecnicamente su un genitore pagea di quelle pagine, tuttavia c'è un modo in cui posso ingannare / correggere / schivare in modo che le pagine vengano visualizzate come genitori?

L'ho raggiunto bene utilizzando le pagine per i membri del team, tuttavia è stato scelto un tipo di post personalizzato per un facile utilizzo da parte dell'amministratore.

Grazie ragazzi + ragazze!


devi impostare l'ID della pagina dei membri del team come tipo di post personalizzato post_parent.
Bainternet,

Non vedo quell'opzione nella register_post_typedocumentazione, puoi aiutarmi?
Ben Everard,

Risposte:


6

Quando si lavora con le pagine è possibile selezionare una pagina padre e quel valore viene salvato come numero ID della pagina padre nelle pagine figlio post_parent campo nel database.

Nel tuo caso, stai utilizzando un tipo di post personalizzato, quindi dovrai creare il tuo metabox per la pagina principale; qualcosa di simile a:

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

Non ha niente a che fare con register_post_type. Stai inducendo WordPress a pensare che si tratti di una pagina figlio di un altro tipo di post (pagina).


1
Giusto, quindi posso vedere come questo "WordPress" crede a pensare che una pagina specifica sia la pagina principale, tuttavia non aggiunge la classe padre della pagina alla pagina madre quando io wp_list_pages.
Ben Everard,

1
Ho notato che questo rovina anche la mia struttura di lumache / permalink ...: S
Ben Everard

2
sto cercando di ottenere la stessa cosa di Ben ma lo uso wp_nav_menu- il post_parent riguarda / membri del team ma la navigazione evidenzia l'elemento principale dei miei post sul blog "normali" ... hai idea di come potrei risolvere questo problema?
becco

@BenEverard: hai trovato una soluzione per il disordine della struttura del permalink?
abaumg,

0

Sono andato con un walker personalizzato per ottenere qualcosa di simile ... evita le necessità di campi personalizzati, ma tutti i post di un tipo devono trovarsi sotto lo stesso punto nella struttura della pagina.

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

Disclaimer: dopo averlo provato, questo mi sembra un problema non più esistente, perché - almeno per me - funziona solo sulla mia installazione di WP 3.9.2. Tuttavia, non è stato possibile trovare un tracker di bug appropriato.


Ho messo insieme un piccolo plugin per testare questo, che potrebbe aiutare qualcuno. Ma come ho detto in precedenza, non ho potuto riprodurre il problema in un'installazione wordpress corrente. Ho separato il plug-in in quattro file, stanno andando insieme in una directory all'interno della directory del plug-in.

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



  • Questo è un esempio di codice in qualche modo generalizzato.
  • Deve essere adattato al caso d'uso reale.

0

Una possibile soluzione è ogni volta che viene salvato il tipo di post personalizzato, è possibile impostare il suo 'genitore in modo about/team-membersprgrammatico.

Ecco i passaggi:

  1. Puoi usare l' hook save_post per 'catturare' ogni volta che qualcuno prova a salvare un post.
  2. Se quel post è il tipo di post personalizzato che stai cercando, procedi.
  3. Assicurati di impostare il genitore del post personalizzato sulla pagina desiderata (puoi codificare l'ID pagina purché non lo elimini). Puoi usare wp_update_post per salvare il genitore (non l'ho provato da solo, ma non vedo perché non dovrebbe funzionare).

Mi piacerebbe molto vedere un po 'di codice per questo! Sarebbe perfetto, ma non riesco a farlo funzionare mysef.
Johan Dahl,

0

Ho avuto un po 'più di tempo per scavare in questo me stesso (scusate se ho perso tempo a nessuno), e ho pensato che per me il modo migliore per risolvere il problema di evidenziazione sarebbe un po' rifare ciò che _wp_menu_item_classes_by_context()sta facendo, che è iterato su tutto genitori e antenati della voce di menu che funge da genitore del mio tipo di post personalizzato e aggiunge le classi in modo appropriato.

Dato che volevo anche che la pagina principale per il mio tipo di post personalizzato fosse fissa e facilmente modificabile senza dover aggiornare tutti i post una volta modificato il genitore, ho deciso di utilizzare un'opzione invece di popolare il post_parent campo dei miei post di tipo post personalizzato. Ho usato ACF per questo dato che lo sto usando comunque nel mio tema, ma ovviamente anche l'uso dell'opzione WordPress predefinita lo farebbe.

Per le mie esigenze ho potuto utilizzare il wp_nav_menu_objectsfiltro. Inoltre, ho dovuto filtrare l' page_for_postsopzione in modo che restituisse un valore errato / vuoto, in questo modo si evita di evidenziare anche la pagina dei post predefiniti.

Nota che non sono andato fino in fondo, il filtro aggiunge solo le classi current-menu-ancestore current-menu-parent, poiché questo era abbastanza per le mie esigenze!

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

Per completezza, quando si popola post_parent(vedere la risposta di @ Bainternet ) invece di utilizzare le opzioni, il recupero dell'ID principale potrebbe essere simile al seguente:

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

Non hai perso tempo :) Un'altra cosa, sei sicuro che sia ancora un problema? Perché sulla mia installazione di WP 3.9.2 non sono riuscito a riprodurlo. L'evidenziazione della voce di menu corretta ha funzionato immediatamente.
Nicolai,

Sì, è sicuramente ancora un problema @ialocin. Potrebbe essere che lo stai testando con un menu di livello 0 e il tipo di post predefinito?
ndm,

No, l'ho provato con il codice pubblicato nella mia risposta. Quindi, con un tipo di post personalizzato e come voce di menu di 1 ° e 2 ° livello in una pagina dal tipo di post corrispondente. Ho usato i temi in bundle core di wordpress per testarlo.
Nicolai,

@ialocin Non sono sicuro di averti compreso correttamente, perché " provato con il codice pubblicato " e " out of the box " si escludono a vicenda? ;) Ti riferisci solo al tipo di post personalizzato, non alla correzione dell'evidenziazione?
ndm,

Giusto :) Ok, per essere precisi, per lo scenario è necessario un CPT, quindi ovviamente l'ho registrato. L'evidenziazione funziona senza l'uso della meta box e della correzione dell'evidenziazione. Ad esempio con una struttura di menu: nonno (pagina)> genitore (pagina)> qualcosa (posta)> un'altra cosa (cpt)> un'altra cosa (cpt) - ogni elemento ottiene le classi css corrette; tema usato qui venti tredici.
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

MODIFICA 1:

Poiché i puntatori non funzionano:

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

Ecco qua. Aggiunto nel gancio del filtro wp_nav_menu_objects.
aifrim,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.