Intégration d'un type de publication personnalisé dans une hiérarchie de pages

14

Je crée un thème avec un type de publication personnalisé pour les membres de l'équipe, j'ai également la structure de page suivante:

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 troisième structure utilise ici les pages à propos et des membres de l'équipe, mais continue à utiliser le slug de type de publication personnalisé pour donner l'impression que ses parents sont membres de l'équipe et environ. J'ai atteint cet objectif en définissant les options suivantes sur le type de publication personnalisé:

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

Cela fonctionne très bien, mais lorsque je descends au niveau de publication des membres de l'équipe, je n'obtiens plus les classes page actuelle et ancêtre actuel sur les pages parent. Je sais pourquoi c'est parce que nous ne sommes pas techniquement sur un parent pagea de ces pages, mais y a-t-il un moyen de tromper / corriger / héberger pour que les pages apparaissent en tant que parents?

J'avais bien réussi cela en utilisant des pages pour les membres de l'équipe, mais un type de publication personnalisé a été choisi à la place pour une utilisation facile pour l'administrateur.

Merci les gars + les filles!

Ben Everard
la source
vous devez définir l'ID de page des membres de l'équipe comme type de publication personnalisé post_parent.
Bainternet
Je ne vois pas cette option dans la register_post_typedocumentation, pouvez-vous m'aider?
Ben Everard

Réponses:

6

Lorsque vous travaillez avec des pages, vous pouvez sélectionner une page parent et cette valeur est enregistrée en tant que numéro d'identification de la page parent dans le post_parentchamp de la page enfant dans la base de données.

Dans votre cas, vous utilisez un type de publication personnalisé, vous devez donc créer votre propre métabox pour la page parent; quelque chose comme:

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

Cela n'a rien à voir avec register_post_type. Vous incitez WordPress à penser qu'il s'agit d'une page enfant d'un autre type de publication (page).

Bainternet
la source
1
Righto, donc je peux voir comment ce "tromper" WordPress pour penser qu'une page spécifique est son parent, mais il n'ajoute pas la classe parent de page à la page parent quand je wp_list_pages.
Ben Everard
1
J'ai remarqué que cela gâchait aussi ma structure de slug / permalink ...: S
Ben Everard
2
j'essaie de réaliser la même chose que Ben mais j'utilise wp_nav_menu- le post_parent concerne / team-members mais la navigation met en évidence l'élément parent de mes articles de blog "normaux" ... une autre idée comment je pourrais résoudre ce problème?
pkyeck
@BenEverard: Avez-vous trouvé une solution pour le désordre de la structure de permaliens?
abaumg
0

Je suis allé avec un déambulateur personnalisé pour réaliser quelque chose de similaire ... évite les besoins de champs personnalisés, mais tous les messages d'un type doivent se trouver en dessous du même point dans l'arborescence de la page.

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
la source
0

Avis de non-responsabilité: après l'avoir essayé, cela ne semble plus être un problème, car - au moins pour moi - cela fonctionne uniquement sur mon installation WP 3.9.2. Impossible de trouver un traqueur de bogues correspondant.


J'ai sorti ensemble un petit plugin pour tester cela, ce qui pourrait aider quelqu'un. Mais comme je l'ai dit dans l'avertissement ci-dessus, je ne pouvais pas reproduire le problème dans une installation wordpress actuelle. J'ai séparé le plugin en quatre fichiers, ils vont ensemble dans un répertoire à l'intérieur du répertoire du plugin.

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



  • Il s'agit d'un exemple de code quelque peu généralisé.
  • Il doit être adapté au cas d'utilisation réel.
Nicolai
la source
0

Une solution possible consiste à chaque fois que le type de publication personnalisé est enregistré, vous pouvez définir son parent de manière about/team-memberspragmatique.

Voici les étapes:

  1. Vous pouvez utiliser le crochet save_post pour «attraper» chaque fois que quelqu'un essaie d'enregistrer un message.
  2. Si ce message est le type de message personnalisé que vous recherchez, continuez.
  3. Assurez-vous de définir le parent de la publication personnalisée sur la page souhaitée (vous pouvez coder en dur l'ID de page tant que vous ne le supprimez pas). Vous pouvez utiliser wp_update_post pour enregistrer le parent (je n'ai pas essayé cela moi-même, mais je ne vois pas pourquoi cela ne devrait pas fonctionner).
Shahar Dekel
la source
J'aimerais beaucoup voir du code pour ça! Ce serait parfait, mais je ne peux pas le faire fonctionner mysef.
Johan Dahl
0

J'ai eu un peu plus de temps pour creuser cela moi-même (désolé si j'ai perdu le temps de quelqu'un), et je me suis dit que pour moi, la meilleure façon de résoudre le problème de mise en évidence serait de refaire ce qui _wp_menu_item_classes_by_context()est en train de faire, c'est-à-dire d'itérer sur tout parents et ancêtres de l'élément de menu qui agit en tant que parent de mon type de publication personnalisé, et ajoutez les classes de manière appropriée.

Étant donné que je voulais également que la page parent de mon type de publication personnalisé soit fixe et facilement modifiable sans avoir à mettre à jour toutes les publications une fois le parent modifié, j'ai décidé d'utiliser une option au lieu de remplir le post_parentchamp de mes publications de type de publication personnalisé. J'ai utilisé ACF pour cela depuis que je l'utilise de toute façon dans mon thème, mais utiliser la fonctionnalité option WordPress par défaut le ferait bien sûr aussi.

Pour mes besoins, je pouvais utiliser le wp_nav_menu_objectsfiltre. De plus, j'ai dû filtrer l' page_for_postsoption pour qu'elle renvoie une valeur faussement / vide, cela évite que la page de messages par défaut ne soit également mise en surbrillance.

Notez que je ne suis pas allé jusqu'au bout, le filtre n'ajoute que les classes current-menu-ancestoret current-menu-parent, car cela suffisait à mes besoins!

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

Par souci d'exhaustivité, lors du remplissage post_parent(voir la réponse de @ Bainternet ) au lieu d'utiliser des options, la récupération de l'ID parent pourrait ressembler à ceci:

/**
 * 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
la source
Vous n'avez pas perdu mon temps :) Autre chose, êtes-vous sûr que c'est toujours un problème? Parce que sur mon installation WP 3.9.2 je n'ai pas pu le reproduire. La mise en surbrillance de l'élément de menu correct a fonctionné juste à la sortie de la boîte.
Nicolai
Oui, c'est définitivement un problème @ialocin. Serait-ce que vous testez cela avec un menu de niveau 0 et le type de message par défaut?
ndm
Non, j'ai essayé avec le code affiché dans ma réponse. Donc, avec un type de publication personnalisé et comme élément de menu de 1er et 2e niveau vers une page du type de publication correspondant. J'ai utilisé les thèmes de base de wordpress pour le tester.
Nicolai
@ialocin Je ne sais pas si je vous comprends bien, parce que « essayé avec le code affiché » et « de la boîte » sont un peu mutuellement exclusifs? ;) Faites-vous uniquement référence au type de publication personnalisé, pas au correctif de mise en évidence?
ndm
À droite :) Ok, pour être précis, pour le scénario un CPT est nécessaire, donc bien sûr je l'ai enregistré. La mise en évidence fonctionne sans l'utilisation de la méta-boîte et du correctif de mise en évidence. Par exemple, avec une structure de menu: grand-parent (page)> parent (page)> quelque chose (poster)> autre chose (cpt)> une autre chose (cpt) - chaque élément obtient la ou les classes css correctes; thème utilisé ici vingt-treize.
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:

Étant donné que les pointeurs ne fonctionnent pas:

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
la source
Voilà. Ajout dans le crochet du filtre wp_nav_menu_objects.
aifrim