Integrar un tipo de publicación personalizada en una jerarquía de páginas

14

Estoy creando un tema con un tipo de publicación personalizado para los miembros del equipo, también tengo la siguiente estructura de página:

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 tercera estructura aquí usa las páginas acerca de y miembros del equipo, pero luego usa el slug de tipo de publicación personalizado para que parezca que sus padres son miembros del equipo y acerca de. Lo logré configurando las siguientes opciones en el tipo de publicación personalizada:

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

Esto funciona muy bien, sin embargo, cuando llego al nivel de publicación de miembro del equipo, ya no obtengo las clases de página actual, ancestro actual en las páginas principales. Sé por qué es así, porque técnicamente no estamos en una página padre de esas páginas, sin embargo, ¿hay alguna manera de que pueda engañar / arreglar / bodge para que las páginas parezcan padres?

Lo había logrado muy bien mediante el uso de páginas para los miembros del equipo, sin embargo, se eligió un tipo de publicación personalizada para facilitar el uso del administrador.

Gracias chicos + chicas!

Ben Everard
fuente
debes establecer la identificación de la página de miembros del equipo como tu tipo de publicación personalizada post_parent
Bainternet
No veo esa opción en la register_post_typedocumentación, ¿me pueden ayudar?
Ben Everard

Respuestas:

6

Al trabajar con páginas, puede seleccionar una página principal y ese valor se guarda como el número de identificación de la página principal en el post_parentcampo de la página secundaria en la base de datos.

En su caso, está utilizando un tipo de publicación personalizado, por lo que necesitaría crear su propio metabox para la página principal; algo como:

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

No tiene nada que ver con eso register_post_type. Estás engañando a WordPress para que piense que es una página secundaria de otro tipo de publicación (página).

Bainternet
fuente
1
Bien, entonces puedo ver cómo esto "engaña" a WordPress para pensar que una página específica es su padre, sin embargo, no está agregando la clase padre de la página a la página principal cuando yo wp_list_pages.
Ben Everard
1
He notado que esto también interfiere con mi estructura slug / permalink ...: S
Ben Everard
2
Estoy tratando de lograr lo mismo que Ben, pero lo uso wp_nav_menu: post_parent trata sobre / team-members pero la navegación resalta el elemento principal de mis publicaciones de blog "normales" ... ¿alguna otra idea de cómo podría solucionar esto?
pkyeck
@BenEverard: ¿Encontraste una solución para el desorden de la estructura de enlaces permanentes?
abaumg
0

Fui con un andador personalizado para lograr algo similar ... evita las necesidades de campos personalizados, pero todas las publicaciones de un tipo deben ubicarse debajo del mismo punto en el árbol de la página.

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

Descargo de responsabilidad: después de intentarlo, este parece un problema que ya no existe, porque, al menos para mí, simplemente funciona en mi instalación de WP 3.9.2. Sin embargo, no pude encontrar un rastreador de errores correspondiente.


Tengo un pequeño complemento para probar esto, que podría ayudar a alguien. Pero como dije en el descargo de responsabilidad anterior, no pude reproducir el problema en una instalación actual de WordPress. He separado el complemento en cuatro archivos, van juntos en un directorio dentro del directorio del complemento.

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



  • Este es un ejemplo de código algo generalizado.
  • Tiene que ajustarse al caso de uso real.
Nicolai
fuente
0

Una posible solución es que siempre que se guarde el tipo de publicación personalizada, puede configurar su 'padre para que sea about/team-membersprogramáticamente.

Aquí están los pasos:

  1. Puede usar el gancho save_post para 'atrapar' cada vez que alguien intenta guardar una publicación.
  2. Si esa publicación es el tipo de publicación personalizada que busca, continúe.
  3. Asegúrese de establecer el elemento primario de la publicación personalizada en la página que desee (puede codificar la ID de la página siempre que no la elimine). Puede usar wp_update_post para guardar el padre (no lo he intentado yo mismo, pero no veo por qué no debería funcionar).
Shahar Dekel
fuente
¡Me gustaría mucho ver un código para esto! Esto sería perfecto, pero no puedo hacer que funcione mysef.
Johan Dahl
0

Tuve más tiempo para profundizar en esto (lo siento si perdí el tiempo de alguien), y pensé que para mí, la mejor manera de resolver el problema de resaltado sería volver a hacer lo que _wp_menu_item_classes_by_context()está haciendo, es decir, repetir todo padres y antepasados ​​del elemento de menú que actúa como el padre de mi tipo de publicación personalizada, y agregue clases adecuadamente.

Dado que también quería que la página principal para mi tipo de publicación personalizada fuera fija y fácil de cambiar sin tener que actualizar todas las publicaciones una vez que el padre cambia, he decidido usar una opción en lugar de completar el post_parentcampo de mis publicaciones de tipo de publicación personalizada. He usado ACF para eso ya que lo estoy usando en mi tema de todos modos, pero usar la funcionalidad de la opción predeterminada de WordPress por supuesto también lo haría.

Para mis necesidades podría hacer uso de wp_nav_menu_objects filtro. Además, tuve que filtrar la page_for_postsopción para que devuelva un valor falso / vacío, esto evita que la página de publicaciones predeterminada también se destaque.

Tenga en cuenta que no fui hasta el final, el filtro solo agrega el current-menu-ancestorcurrent-menu-parent clases y , ¡ya que esto fue suficiente para mis necesidades!

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

En aras de la exhaustividad, al completar post_parent(ver la respuesta de @ Bainternet ) en lugar de usar opciones, luego recuperar la ID principal podría verse así:

/**
 * 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
fuente
No has perdido mi tiempo :) Otra cosa, ¿estás seguro de que esto sigue siendo un problema? Porque en mi instalación de WP 3.9.2 no pude reproducirlo. Destacar el elemento de menú correcto funcionó de inmediato.
Nicolai
Sí, definitivamente sigue siendo un problema @ialocin. ¿Podría ser que está probando esto con un menú de nivel 0 y el tipo de publicación predeterminado?
ndm
No, lo probé con el código publicado en mi respuesta. Entonces, con un tipo de publicación personalizado y como elemento de menú de primer y segundo nivel a una página del tipo de publicación correspondiente. Utilicé los temas incluidos en el núcleo de WordPress para probarlo.
Nicolai
@ialocin No estoy seguro si he entendido bien, porque " tratado con el código escrito " y " fuera de la caja " son un poco mutuamente excluyentes? ;) ¿Se refiere solo al tipo de publicación personalizada, no a la corrección de resaltado?
ndm
Correcto :) Ok, para ser precisos, para el escenario se necesita un CPT, así que, por supuesto, lo registré. El resaltado funciona sin el uso del cuadro meta y la corrección de resaltado. Por ejemplo, con una estructura de menú: abuelo (página)> padre (página)> algo (publicación)> otra cosa (cpt)> una cosa más (cpt): cada elemento obtiene las clases css correctas; tema utilizado aquí veinte trece.
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

EDITAR 1:

Como los punteros no funcionan:

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
fuente
Ahí tienes. Lo agregó dentro del gancho de filtro wp_nav_menu_objects.
aifrim