web-dev-qa-db-fra.com

définir un CPT en tant que parent d'une page

J'ai un type de message personnalisé appelé Event qui est enregistré comme ceci:

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

J'aimerais pouvoir définir un événement comme parent d'une page ordinaire. En d'autres termes, j'aimerais que tous mes événements apparaissent dans l'élément Parent select sous le Page Attributes.

J'ai lu de nombreux articles sur la manière de définir un CPT en tant qu'enfant d'une page, mais pas l'inverse.

4
Cyclonecode

Le défi que nous essayons de relever est de savoir comment:

  • pas supprimer ou remplacer la attribut de page} méta-box actuelle

  • conservez les informations hiérarchiques dans la liste déroulante.

Nous supposons que nous sommes sur l'écran d'édition d'une seule page et que nous souhaitons ajouter le type d'article event à la pageliste déroulante des parents.

La liste déroulante est affichée à l'aide de la fonction wp_dropdown_pages(), qui appelle la fonction get_pages(). Il ne prend en charge qu'un seul type de message.

Voici deux façons de le faire:

Méthode n ° 1 - Modifier SQL

Voici une méthode expérimentale, car il traite des modifications SQL.

Comme il n’existe aucun moyen évident de filtrer la requête SQL générée dans get_pages(), nous pouvons utiliser le filtre query général à nos besoins.

Pour rendre le menu déroulant plus convivial, lorsqu'il contient des titres de plusieurs types de publication, nous utilisons le filtre list_pages pour ajouter des informations sur le type de publication.

Exemple:

Donc, au lieu d’afficher les options telles que:

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

on a:

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

Plugin de démonstration:

Voici l'extrait de code qui pourrait être placé dans un plugin à des fins de test:

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

où:

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

et un peu de nettoyage:

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éthode n ° 2 - Utilisez uniquement wp_dropdown_pages()

Ici, nous laissons __wp_dropdown_pages() courir deux fois puis le fusionner en un seul menu déroulant; une fois pour le type de poste page et encore une fois pour le type de poste 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;
}

Ici, les deux listes déroulantes hiérarchiques sont séparées par l’option vide === Sélectionnez Événements ci-dessous: ===.

Exemple:

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

Ajuster la requête principale

Supposons maintenant que nous créons une page appelée Of Monsters And Men - Info avec le slug omam-info et sélectionnons l'événement Of Monsters And Men en tant que parent, avec le slug omam.

Alors le chemin serait

example.tld/omam/omam-info

mais cela donne une erreur 404. La raison en est que la vérification get_page_path() dans la classe \WP_Query, pour la requête principale, échoue:

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

En effet, ici get_page_by_path() ne vérifie que les types d'articles page et attachment, et non le type d'article event. Malheureusement, il n'y a pas de filtre explicite pour changer cela.

Nous pourrions bien sûr utiliser le filtre query, comme nous l'avons fait ci-dessus, mais essayons une autre solution de contournement.

Nous pouvons essayer d'ajuster les propriétés non assignées queried_object_id et queried_object_id de l'objet \WP_Query avec:

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

Cela devrait également prendre en charge n'importe quel nombre de parents d'événement, comme la hiérarchie suivante:

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

Remarque

Pour les listes déroulantes parent dans l'écran edit.php, nous pourrions utiliser le filtre quick_edit_dropdown_pages_args au lieu du filtre page_attributes_dropdown_pages_args que nous avons utilisé ci-dessus.

5
birgire

Premièrement, vous devez supprimer le noyau Attributs de page Meta Box, puis ajouter le vôtre en gardant tout intact, sauf en modifiant le type de message parent. Voici mes codes, modifiés pour fonctionner avec votre type de poste events. J'ai essayé de le faire copier/coller, mais si vous rencontrez des erreurs, merci de me le faire savoir.

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

EDIT: Je viens de remarquer qu'il existe un filtre pour modifier les arguments par défaut dans la méta-boîte de l'attribut de page:

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

Donc, il pourrait y avoir un moyen très facile de faire ce que vous voulez.

1
Abhik