establecer un CPT como padre de una página

4

Tengo un tipo de publicación personalizado llamado Event que está registrado de esta manera:

add_action('init', 'register_custom_post_types');

function register_custom_post_types() {
   $event_capabilities = array(
      'publish_posts' => 'publish_events',
      'edit_posts' => 'edit_events',
      'edit_others_posts' => 'edit_others_event',
      'delete_posts' => 'delete_events',
      'delete_published_posts' => 'delete_published_events',
      'delete_others_posts' => 'delete_others_events',
      'read_private_posts' => 'read_private_events',
      'edit_post' => 'edit_event',
      'delete_post' => 'delete_event',
      'read_post' => 'read_event',
   );
   register_post_type('event',
      array(
        'labels' => array(
            'name' => __( 'Event' )
         ),
        'rewrite' => 'event',
        'public' => true,
        'has_archive' => true,
        'show_ui' => true,
        'menu_position' => 8,
        'capability_type' => array('event', 'events'),
        'capabilities' => $event_capabilities,
        'supports' => array('title', 'thumbnail', 'page-attributes'),
        'map_meta_cap' => true,
        'hierarchical' => true,
      )
   );
}

Me gustaría poder establecer un evento como elemento principal de una página normal. En otras palabras, me gustaría que todos mis eventos se muestren en el elemento de selección Parent debajo de Page Attributes .

He leído muchas publicaciones sobre cómo establecer un CPT como elemento secundario de una página, pero no al revés.

    
pregunta Cyclonecode 30.09.2015 - 09:21

2 respuestas

5

El desafío que intentamos asumir aquí es cómo:

  • no elimina ni reemplaza el actual Atributo de página meta box

  • mantenga la información jerárquica en el menú desplegable.

Suponemos que estamos en la pantalla de edición de una sola página y queremos agregar el tipo de publicación event al page menú desplegable principal .

La lista desplegable se muestra mediante la función wp_dropdown_pages() , que llama a la función get_pages() . Solo soporta un único tipo de publicación.

Aquí hay dos formas de hacer esto:

Método # 1 - Modificar SQL

Esta es una forma experimental, ya que trata con las modificaciones de SQL.

Ya que no hay una forma obvia de filtrar la consulta SQL generada en get_pages() , podemos usar el filtro general query para nuestras necesidades.

Para hacer que el menú desplegable sea más fácil de usar, cuando contiene títulos de varios tipos de publicaciones, usamos el filtro list_pages , para anteponer la información del tipo de publicación.

Ejemplo :

Entonces, en lugar de mostrar las opciones como:

"(no parent)"
"About the Music Hall"
    "History"
    "Location"
"Concerts"
    "Of Monsters and Men"
    "Vienna Philharmonic Orchestra"
"Tickets"

obtenemos:

"(no parent)"
"page - About the Music Hall"
    "page  - History" 
    "page  - Location"
"event - Concerts"
    "event - Of Monsters and Men"
    "event - Vienna Philharmonic Orchestra"
"page - Tickets"

Complemento de demostración:

Aquí está el fragmento de código que se podría colocar en un complemento para la prueba:

/**
 * Add the hierarchical 'event' post type, to the 'page' parent drop-down.
 *
 * @link http://wordpress.stackexchange.com/a/204439/26350
 */ 
is_admin() && add_filter( 'page_attributes_dropdown_pages_args', function( $dropdown_args, $post )
{
     // Only do this for the 'page' post type on the single edit 'page' screen
     if( 'page' === get_current_screen()->id && 'page' === $post->post_type )
     {
        // Modify the options' titles
        add_filter( 'list_pages', 'wpse_add_post_type_info_in_options', 10, 2 );

        // Modify the get_pages() query
        add_filter( 'query', function( $sql )
        {
            // Only run this once
            if( ! did_action( 'wpse_change_cpt' ) )
            {
                do_action( 'wpse_change_cpt' );

                // Adjust the post type part of the query - add the 'event' post type
                $sql = str_replace( 
                    "post_type = 'page' ", 
                    "post_type IN ( 'event', 'page' ) ", 
                    $sql 
                );
            }
            return $sql;
        } );
    }
    return $dropdown_args;
}, 10, 2 );

donde:

function wpse_add_post_type_info_in_options ( $title, $page )
{
    return $page->post_type . ' - ' . $title;
}

y un poco de limpieza:

add_filter( 'wp_dropdown_pages', function( $output )
{
    if( did_action( 'wpse_change_cpt' ) )
        remove_filter( 'list_pages', 'wpse_add_post_type_info_in_options', 10, 2 );

    return $output;
} );

Método # 2: solo usa wp_dropdown_pages()

Aquí dejamos que wp_dropdown_pages() se ejecute dos veces y luego lo combinamos en un solo desplegable; una vez para el tipo de publicación page y otra vez para el tipo de publicación event :

/**
 * Add the hierarchical 'event' post type, to the 'page' parent drop-down.
 *
 * @link http://wordpress.stackexchange.com/a/204439/26350
 */ 
is_admin() && add_filter( 'page_attributes_dropdown_pages_args', 'wpse_attributes', 99, 2 );

function wpse_attributes( $dropdown_args, $post )
{
    // Run this filter callback only once
    remove_filter( current_filter(), __FUNCTION__, 99 );

    // Only do this for the 'page' post type on the edit page screen
    if( 'page' === get_current_screen()->id && 'page' === $post->post_type )
    {
        // Modify the input arguments, for the 'event' drop-down
        $modified_args = $dropdown_args;
        $modified_args['post_type'] = 'page';
        $modified_args['show_option_no_change'] = __( '=== Select Events here below: ===' );            
        $modified_args['show_option_none'] = false;

        // Add the 'event' drop-down
        add_filter( 'wp_dropdown_pages', function( $output ) use ( $modified_args )
        {
            // Only run it once
            if( ! did_action( 'wpse_dropdown' ) )
            {
                do_action( 'wpse_dropdown' );

                // Create our second drop-down for events
                $output .= wp_dropdown_pages( $modified_args );

                // Adjust the output, so we only got a single drop-down
                $output = str_replace( 
                    [ "<select name='parent_id' id='parent_id'>", "</select>"],
                    '', 
                    $output 
                );
                $output = "<select name='parent_id' id='parent_id'>" . $output . "</select>";
            }
            return $output;
        } );
    }
    return $dropdown_args;
}

Aquí, los dos menús desplegables jerárquicos están separados por la opción vacía === Seleccionar eventos a continuación: === .

Ejemplo :

"(no parent)"
"About the Music Hall"
    "History"
    "Location"
"Tickets"
"=== Select Events here below: ==="
"Concerts"
    "Of Monsters and Men"
    "Vienna Philharmonic Orchestra"

Ajustando la consulta principal

Supongamos ahora que creamos una página llamada Of Monsters And Men - Info con el slug omam-info y seleccionamos el evento Of Monsters And Men como padre, con el omam slug.

Entonces el camino sería

example.tld/omam/omam-info

pero esto da un error 404. La razón es que la comprobación get_page_path() dentro de la clase \WP_Query , para la consulta principal, falla:

if ( '' != $qv['pagename'] ) {
    $this->queried_object = get_page_by_path($qv['pagename']);
    if ( !empty($this->queried_object) )
        $this->queried_object_id = (int) $this->queried_object->ID;
    else
        unset($this->queried_object);

Esto se debe a que aquí get_page_by_path() solo verifica los tipos de publicación page y attachment , no el tipo de publicación event . Desafortunadamente no hay un filtro explícito para cambiar eso.

Por supuesto, podríamos usar el filtro query , como hicimos anteriormente, pero intentemos otra solución.

Podemos intentar ajustar las propiedades no asignadas queried_object_id y queried_object_id del objeto \WP_Query con:

/**
 * Support for page slugs with any kind event parent hierarchy
 *
 * @link http://wordpress.stackexchange.com/a/204439/26350
 */
add_action( 'pre_get_posts', function( \WP_Query $q )
{
    if( 
            ! is_admin()               // Front-end only
        &&  $q->is_main_query()        // Target the main query
        &&  $q->get( 'pagename' )      // Check for pagename query variable
        &&  ! $q->get( 'post_type' )   // Target the 'page' post type
        && $page = get_page_by_path( $q->get( 'pagename' ), OBJECT, [ 'page', 'event', 'attachment' ] ) 
    ) {
        if( is_a( $page, '\WP_Post' ) )
        {                
            $q->queried_object_id = $page->ID;
            $q->queried_object = $page;
        }
    }
} );

Esto también debería admitir cualquier cantidad de padres de eventos, como la siguiente jerarquía:

ecample.tld/event-grandparent/event-parent/event-child/page-slug

Nota

Para los desplegables principales en la pantalla edit.php , podríamos usar el filtro quick_edit_dropdown_pages_args , en lugar del filtro page_attributes_dropdown_pages_args que usamos anteriormente.

    
respondido por el birgire 02.10.2015 - 23:21
1

Fisrt, debe eliminar el cuadro Atributos de la página del núcleo, luego agregar el suyo manteniendo todo intacto, excepto la modificación del tipo de publicación principal. Aquí están mis códigos, ajustados para trabajar con su tipo de publicación events . Intenté hacerlo listo para copiar / pegar, sin embargo, si encuentra errores, hágamelo saber.

/* Remove the core Page Attribute Metabox */
function event_remove_pa_meta_boxes() {
  remove_meta_box( 'pageparentdiv', 'page', 'side' );
}
add_action( 'do_meta_boxes', 'event_remove_pa_meta_boxes' );

/* Set the Page Attribute Metabox again*/
function events_pa_meta_box( $post ) {

    add_meta_box(
        'event-select',
        __( 'Page Attributes', 'textdomain' ),
        'events_selectors_box',
        'page',
        'side',
        'core'
    );
}
add_action( 'add_meta_boxes_page', 'events_pa_meta_box' );

/* Recreate the meta box. */
function events_selectors_box( $post ) {

    /* Set Events as Post Parent */
    $parents = get_posts(
        array(
            'post_type'   => 'event', 
            'orderby'     => 'title', 
            'order'       => 'ASC', 
            'numberposts' => -1 
        )
    );

    echo '<p><strong>Parent</strong></p><label class="screen-reader-text" for="parent_id">Parent</label>';
    if ( !empty( $parents ) ) {

        echo '<select id="parent_id" name="parent_id">';

        foreach ( $parents as $parent ) {
            printf( '<option value="%s"%s>%s</option>', esc_attr( $parent->ID ), selected( $parent->ID, $post->post_parent, false ), esc_html( $parent->post_title ) );
        }

        echo '</select>';

    } else {

        echo '<p>Please <a href="' . admin_url( "post-new.php?post_type=event" ) . '">create an event first</a>.</p>';

    }

    /* Page Templates */

    if ( 'page' == $post->post_type && 0 != count( get_page_templates( $post ) ) && get_option( 'page_for_posts' ) != $post->ID ) {

        $template = !empty($post->page_template) ? $post->page_template : false;

        echo '<p><strong>Template</strong></p><label class="screen-reader-text" for="page_template">Page Template</label>';

        echo '<select id="page_template" name="page_template">';

        $default_title = apply_filters( 'default_page_template_title',  __( 'Default Template' ), 'meta-box' );

        echo '<option value="default">' . esc_html( $default_title ) . '</option>'      

            page_template_dropdown($template);

        echo '</select>';

    }

    /* Page Order */
    echo '<p><strong>' . _e('Order') .'</strong></p>';

    echo '<label class="screen-reader-text" for="menu_order">'. _e('Order') . '</label><input name="menu_order" type="text" size="4" id="menu_order" value="'. esc_attr($post->menu_order) .'" />';

    /* Help Paragraph */
    if ( 'page' == $post->post_type && get_current_screen()->get_help_tabs() ) { ?>
        echo '<p>' . _e( 'Need help? Use the Help tab in the upper right of your screen.' ) . '</p>';
    }
}  

EDITAR: Acabo de notar que hay un filtro disponible para modificar los argumentos predeterminados en el cuadro de meta de atributo de página:

$dropdown_args = array(
    'post_type'        => $post->post_type,
    'exclude_tree'     => $post->ID,
    'selected'         => $post->post_parent,
    'name'             => 'parent_id',
    'show_option_none' => __('(no parent)'),
    'sort_column'      => 'menu_order, post_title',
    'echo'             => 0,
);

$dropdown_args = apply_filters( 'page_attributes_dropdown_pages_args', $dropdown_args, $post );  

Por lo tanto, podría haber una forma súper fácil de hacer lo que quieres.

    
respondido por el Abhik 03.10.2015 - 13:03

Lea otras preguntas en las etiquetas