Integrando um tipo de postagem personalizado em uma hierarquia de páginas

14

Estou criando um tema com um tipo de postagem personalizado para os membros da equipe. Também tenho a seguinte estrutura 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

A terceira estrutura aqui usa as páginas about e membro da equipe, mas continua usando o slug de tipo de postagem personalizado para fazer parecer que os pais são membros da equipe e aproximadamente. Consegui isso definindo as seguintes opções no tipo de postagem personalizada:

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

Isso funciona muito bem, no entanto, quando chego ao nível de postagem dos membros da equipe, não recebo mais as classes da página atual e dos ancestrais atuais nas páginas pai. Eu sei por que isso ocorre, porque não estamos tecnicamente em um pai pagina dessas páginas; no entanto, existe uma maneira de enganar / corrigir / ignorar para que as páginas apareçam como pais?

Eu consegui isso muito bem usando páginas para os membros da equipe; no entanto, um tipo de postagem personalizado foi escolhido para facilitar o uso do administrador.

Obrigado rapazes + raparigas!

Ben Everard
fonte
você precisa definir o ID da página dos membros da equipe como seu tipo de postagem personalizado post_parent.
Bainternet 28/03
Não vejo essa opção na register_post_typedocumentação, você pode ajudar?
Ben Everard 28/03

Respostas:

6

Ao trabalhar com páginas, você pode selecionar uma página pai e esse valor é salvo como o número de identificação da página pai no post_parentcampo da página filha no banco de dados.

No seu caso, você está usando um tipo de postagem personalizado; portanto, você precisará criar sua própria metabox para a página pai; 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;
}

Não tem nada a ver com isso register_post_type. Você está enganando o WordPress a pensar que é uma página filha de outro tipo de postagem (página).

Bainternet
fonte
1
Correto, para que eu possa ver como esse WordPress "engana" pensar que uma página específica é pai, no entanto, não está adicionando a classe pai da página à página pai quando eu wp_list_pages.
Ben Everard 28/03
1
Notei que isso também mexe com a minha estrutura de lesmas / permalink ...: S
Ben Everard 29/11
2
Estou tentando conseguir a mesma coisa que Ben, mas eu uso wp_nav_menu- o post_parent é sobre / team-members, mas a navegação destaca o item pai dos meus posts "normais" do blog ... alguma outra idéia de como eu poderia consertar isso?
Pkeck #
@ BenEverard: Você encontrou uma solução para a confusão da estrutura do permalink?
abaumg
0

Fui com um andador personalizado para obter algo semelhante ... evita as necessidades de campos personalizados, mas todas as postagens de um tipo precisam ficar abaixo do mesmo ponto na árvore de páginas.

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

Isenção de responsabilidade: Depois de tentar, isso me parece um problema não existente, porque - pelo menos para mim - ele funciona na minha instalação do WP 3.9.2. Não foi possível encontrar um rastreador de erros de acordo.


Tenho juntos um pequeno plugin para testar isso, o que pode ajudar alguém. Mas, como eu disse no aviso acima, não consegui reproduzir o problema em uma instalação atual do wordpress. Separei o plugin em quatro arquivos, eles estão indo juntos em um diretório dentro do diretório do 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
    ;
}



  • Este é um exemplo de código um tanto generalizado.
  • Ele deve ser ajustado ao caso de uso real.
Nicolai
fonte
0

Uma solução possível é que, sempre que o tipo de postagem personalizado for salvo, você poderá definir seu pai como about/team-membersprogramaticamente.

Aqui estão os passos:

  1. Você pode usar o gancho save_post para 'capturar' sempre que alguém tentar salvar uma postagem.
  2. Se essa postagem for o tipo de postagem personalizada que você procura, continue.
  3. Certifique-se de definir o pai da postagem personalizada para a página desejada (você pode codificar permanentemente o ID da página, desde que não o exclua). Você pode usar wp_update_post para salvar o pai (não tentei isso sozinho, mas não vejo por que não deve funcionar).
Shahar Dekel
fonte
Eu gostaria muito de ver algum código para isso! Isso seria perfeito, mas não consigo fazê-lo funcionar.
Johan Dahl
0

Eu mesmo tive mais tempo para investigar isso (desculpe se perdi o tempo de alguém), e imaginei que, para mim, a melhor maneira de resolver o problema realçado seria meio que refazer o que _wp_menu_item_classes_by_context()está fazendo, que é repetido por todos. pais e ancestrais do item de menu que atua como pai do meu tipo de postagem personalizado e adiciona classes adequadamente.

Como também queria que a página pai do meu tipo de postagem personalizado fosse corrigida e facilmente alterável sem a necessidade de atualizar todas as postagens depois que o pai for alterado, decidi usar uma opção em vez de preencher o post_parentcampo das minhas postagens personalizadas. Eu usei o ACF para isso, já que eu o uso no meu tema de qualquer maneira, mas o uso da funcionalidade padrão da opção WordPress também o faria.

Para minhas necessidades, eu poderia fazer uso do wp_nav_menu_objectsfiltro. Além disso, eu tive que filtrar a page_for_postsopção para que ela retornasse um valor falso / vazio, o que evita que a página de postagens padrão também seja destacada.

Observe que eu não fui até o fim, o filtro apenas adiciona as classes current-menu-ancestore current-menu-parent, pois isso foi suficiente para minhas necessidades!

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

Por uma questão de integridade, ao preencher post_parent(consulte a resposta da @ Bainternet ) em vez de usar opções, a recuperação do ID pai pode ser algo como isto:

/**
 * 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
fonte
Você não perdeu meu tempo :) Outra coisa, tem certeza que isso ainda é um problema? Porque na minha instalação do WP 3.9.2 não pude reproduzi-lo. Destacar o item de menu correto funcionou imediatamente.
Nicolai
Sim, definitivamente ainda é um problema @ialocin. Será que você está testando isso com um menu no nível 0 e o tipo de postagem padrão?
Ndm 26/08/14
Não, tentei com o código postado na minha resposta. Portanto, com um tipo de postagem personalizado e como item de menu de 1º e 2º nível em uma página do tipo de postagem correspondente. Eu usei os temas principais do wordpress para testá-lo.
Nicolai
@ialocin Não tenho certeza se entendi direito, porque " tentei com o código publicado " e " fora da caixa " são meio que mutuamente exclusivos? ;) Você está se referindo apenas ao tipo de postagem personalizada, não à correção de destaque?
Ndm 26/08/14
Certo :) Ok, para ser mais preciso, para o cenário é necessário um CPT, então é claro que eu o registrei. O destaque funciona sem o uso da meta box e da correção de destaque. Por exemplo, com uma estrutura de menu: avô (página)> pai (página)> algo (post)> outra coisa (cpt)> mais uma coisa (cpt) - cada elemento obtém a (s) classe (s) css correta (s); tema usado aqui vinte e treze.
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:

Como os ponteiros não funcionam:

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
fonte
Ai está. Foi adicionado no gancho de filtro wp_nav_menu_objects.
Aifrim 01/09/2014