Añadir enlaces de tipo de archivo a su menú
Spanish (Español) translation by Elías Nicolás (you can also view the original English article)
Una solicitud común, especialmente para aquellos que han creado tipos de publicación personalizados como 'Noticias' o 'Eventos', es agregar un enlace a la página de archivo de su tipo de publicación en su menú de navegación. Sin embargo, actualmente esto solo se puede hacer ingresando manualmente la URL del archivo de tipo de publicación. Además de ser bastante poco elegante, esta solución tiene algunos inconvenientes: no siempre aparece como "actual", si cambia la estructura de enlace permanente, podría romper el enlace, agregar manualmente las URL es tedioso y el enlace no aparece como " actual 'cuando en una publicación de ese tipo de publicación.
En este tutorial, le mostraré cómo producir un complemento que cree un meta-cuadro en su página Apariencia -> Menú que le permite agregar enlaces de archivo de tipo de publicación. Estos enlaces no sufren de los inconvenientes mencionados anteriormente.
Paso 1 Creando un Plugin
Este complemento se llamará 'Mis enlaces de archivo de tipo de publicación' y, para ello, primero cree una carpeta llamada my-post-type-archive-links en su carpeta /wp-content/plugins/, y dentro de eso cree un archivo my-post-type-archive-links.php. Este archivo es el archivo principal del complemento. Vamos a envolverlo en una clase. Esto es simplemente para que no tengamos que preocuparnos de que nuestros nombres de funciones coincidan con WordPress u otros complementos: simplemente debemos asegurarnos de que nuestro nombre de clase sea único. Agrega lo siguiente a my-post-type-archive-links.php
1 |
<?php
|
2 |
/*
|
3 |
Plugin Name: My Post Type Archive Links
|
4 |
Version: 1.0
|
5 |
Description: Adds a metabox to the Appearance -> Menu page to add post type archive links
|
6 |
Author: Stephen Harris
|
7 |
Author URI: https://profiles.wordpress.org/users/stephenh1988/
|
8 |
*/
|
9 |
|
10 |
class My_Post_Type_Archive_Link { |
11 |
//Everything will go here
|
12 |
}
|
13 |
My_Post_Type_Archive_Link::load(); |
14 |
?>
|
Todo en este tutorial se sentará dentro de esa clase.
Paso 2 Cargando el Plugin
Cuando se carga el archivo del complemento, se activará el método de clase load(). Este método será responsable de agregar acciones y filtros a varios enlaces de WordPress. Repasaremos cada uno de ellos en los pasos posteriores, pero también proporciona un resumen útil. Agregue el siguiente método a nuestra clase:
1 |
public function load(){ |
2 |
// Hook function to add the metabox to the Menu page
|
3 |
add_action( 'admin_init', array(__CLASS__,'add_meta_box')); |
4 |
|
5 |
// Javascript for the meta box
|
6 |
add_action( 'admin_enqueue_scripts', array(__CLASS__,'metabox_script') ); |
7 |
|
8 |
// Ajax callback to create menu item and add it to menu
|
9 |
add_action('wp_ajax_my-add-post-type-archive-links', array( __CLASS__, 'ajax_add_post_type')); |
10 |
|
11 |
// Assign menu item the appropriate url
|
12 |
add_filter( 'wp_setup_nav_menu_item', array(__CLASS__,'setup_archive_item') ); |
13 |
|
14 |
// Make post type archive link 'current'
|
15 |
add_filter( 'wp_nav_menu_objects', array(__CLASS__,'maybe_make_current')); |
16 |
}
|
Resumamos lo que hace cada una de estas partes:
- Añadir una meta-caja - bastante auto explicativo. La función enganchada será responsable de agregar nuestra caja meta.
- Encolar JavaScript: usamos el enlace
admin_enqueue_scriptspara poner en cola nuestro archivo JavaScript. Nuestro JavaScript, cuando haga clic en 'agregar al menú', se activará una solicitud AJAX. - Devolución de llamada AJAX: esta función es responsable de manejar la solicitud AJAX anterior. Creará los elementos del menú y los agregará al menú.
- Configuración de elementos del menú: esto garantiza que cuando el enlace se muestre en su menú, así como el archivo de tipo de publicación.
- Tal vez hacer actual: cada vez que aparece un menú, sus elementos pasan a través de un filtro, nos aseguramos de que la clase '
current-menu-item' se agregue al enlace del tipo de publicación correspondiente.
Paso 3 Agregando el Metabox
Primero definimos nuestro método add_meta_box, que simplemente llama a la función de WordPress add_meta_box(). Los detalles de esta función se han cubierto muchas veces antes, pero si no está seguro, puede leerlo en las páginas del Codex.
1 |
public function add_meta_box() { |
2 |
add_meta_box( 'post-type-archives', __('Post Types','my-post-type-archive-links'),array(__CLASS__,'metabox'),'nav-menus' ,'side','low'); |
3 |
}
|
A continuación, definimos la función de devolución de llamada del meta-cuadro, que es responsable de mostrar el interior del metabox:
1 |
public function metabox( ) { |
2 |
global $nav_menu_selected_id; |
3 |
|
4 |
//Get post types
|
5 |
$post_types = get_post_types(array('public'=>true,'_builtin'=>false), 'object');?> |
6 |
|
7 |
<!-- Post type checkbox list -->
|
8 |
<ul id="post-type-archive-checklist"> |
9 |
<?php foreach ($post_types as $type):?> |
10 |
<li><label><input type="checkbox" value ="<?php echo esc_attr($type->name); ?>" /> <?php echo esc_attr($type->labels->name); ?> </label></li> |
11 |
<?php endforeach;?> |
12 |
</ul><!-- /#post-type-archive-checklist --> |
13 |
|
14 |
<!-- 'Add to Menu' button -->
|
15 |
<p class="button-controls" > |
16 |
<span class="add-to-menu" > |
17 |
<input type="submit" id="submit-post-type-archives" <?php disabled( $nav_menu_selected_id, 0 ); ?> value="<?php esc_attr_e('Add to Menu'); ?>" name="add-post-type-menu-item" class="button-secondary submit-add-to-menu" /> |
18 |
</span>
|
19 |
</p>
|
20 |
<?php
|
21 |
}
|
Este método simplemente obtiene todos los tipos de publicaciones personalizadas públicas con get_post_types() y luego los recorre para crear una lista de casillas de verificación. Cada casilla de verificación tiene el nombre del tipo de publicación como su valor. En el siguiente paso, agregaremos algunos javascript que se activarán cuando un usuario haga clic en el botón 'Agregar al menú'.
Paso 4 El JavaScript
Solo queremos poner en cola nuestro JavaScript en la página de apariencia -> Menú admin. Hemos utilizado el enlace admin_enqueue_scripts que se activa solo en las páginas de administración y pasa el enlace de la página como un argumento. El gancho para la página Apariencia -> Menú es nav-menus.php. Después de poner en cola nuestro script, usamos wp_localize_script para que el nonce esté disponible en nuestro JavaScript. Lo incluimos en la solicitud de AJAX para ayudar a verificar que la acción estaba destinada.
1 |
public function metabox_script($hook) { |
2 |
if( 'nav-menus.php' != $hook ) |
3 |
return; |
4 |
|
5 |
//On Appearance>Menu page, enqueue script:
|
6 |
wp_enqueue_script( 'my-post-type-archive-links_metabox', plugins_url('/metabox.js', __FILE__), array('jquery')); |
7 |
|
8 |
//Add nonce variable
|
9 |
wp_localize_script('my-post-type-archive-links_metabox','MyPostTypeArchiveLinks', array('nonce'=>wp_create_nonce('my-add-post-type-archive-links'))); |
10 |
}
|
En el paso anterior, al botón "Agregar al menú" se le asignó el ID submit-post-type-archives. Ahora usamos jQuery para seleccionar ese botón y, cuando se hace clic en él, enviamos una solicitud AJAX para crear el elemento del menú y agregarlo al menú. La siguiente es la única parte de este tutorial que vive fuera de nuestra clase. Debe ir en un archivo llamado metabox.js, dentro de nuestra carpeta de plug-in.
1 |
jQuery(document).ready(function($) { |
2 |
$('#submit-post-type-archives').click(function(event) { |
3 |
event.preventDefault(); |
4 |
|
5 |
/* Get checked boxes */
|
6 |
var postTypes = []; |
7 |
$('#post-type-archive-checklist li :checked').each(function() { |
8 |
postTypes.push($(this).val()); |
9 |
});
|
10 |
|
11 |
/* Send checked post types with our action, and nonce */
|
12 |
$.post( ajaxurl, { |
13 |
action: "my-add-post-type-archive-links", |
14 |
posttypearchive_nonce: MyPostTypeArchiveLinks.nonce, |
15 |
post_types: postTypes |
16 |
},
|
17 |
|
18 |
/* AJAX returns html to add to the menu */
|
19 |
function( response ) { |
20 |
$('#menu-to-edit').append(response); |
21 |
}
|
22 |
);
|
23 |
})
|
24 |
});
|
Observe la URL a la que enviamos la solicitud a: ajaxurl. No lo hemos definido en ninguna parte. Es una variable global establecida por WordPress en el lado del administrador que apunta a la página que WordPress usa para manejar las solicitudes de AJAX. Cuando se hace clic en el botón de envío, los nombres de los tipos de publicación seleccionados, una acción única y nonce se envían a esta URL. Cuando WordPress recibe la solicitud, activa el enlace wp_ajax_my-add-post-type-archive-links. El nonce es una precaución de seguridad para ayudar a verificar que la acción fue pensada.
Paso 5 La devolución de llamada AJAX
Ahora definimos la función de devolución de llamada AJAX ajax_add_post_type.
1 |
public function ajax_add_post_type() { |
2 |
|
3 |
if ( ! current_user_can( 'edit_theme_options' ) ) |
4 |
die('-1'); |
5 |
|
6 |
check_ajax_referer('my-add-post-type-archive-links', 'posttypearchive_nonce'); |
7 |
|
8 |
require_once ABSPATH . 'wp-admin/includes/nav-menu.php'; |
9 |
|
10 |
if(empty($_POST['post_types'])) |
11 |
exit; |
12 |
|
13 |
// Create menu items and store IDs in array
|
14 |
$item_ids=array(); |
15 |
foreach ( (array) $_POST['post_types'] as $post_type) { |
16 |
$post_type_obj = get_post_type_object($post_type); |
17 |
|
18 |
if(!$post_type_obj) |
19 |
continue; |
20 |
|
21 |
$menu_item_data= array( |
22 |
'menu-item-title' => esc_attr($post_type_obj->labels->name), |
23 |
'menu-item-type' => 'post_type_archive', |
24 |
'menu-item-object' => esc_attr($post_type), |
25 |
'menu-item-url' => get_post_type_archive_link($post_type) |
26 |
);
|
27 |
|
28 |
//Collect the items' IDs.
|
29 |
$item_ids[] = wp_update_nav_menu_item(0, 0, $menu_item_data ); |
30 |
}
|
31 |
|
32 |
// If there was an error die here
|
33 |
if ( is_wp_error( $item_ids ) ) |
34 |
die('-1'); |
35 |
|
36 |
// Set up menu items
|
37 |
foreach ( (array) $item_ids as $menu_item_id ) { |
38 |
$menu_obj = get_post( $menu_item_id ); |
39 |
if ( ! empty( $menu_obj->ID ) ) { |
40 |
$menu_obj = wp_setup_nav_menu_item( $menu_obj ); |
41 |
$menu_obj->label = $menu_obj->title; // don't show "(pending)" in ajax-added items |
42 |
$menu_items[] = $menu_obj; |
43 |
}
|
44 |
}
|
45 |
|
46 |
// This gets the HTML to returns it to the menu
|
47 |
if ( ! empty( $menu_items ) ) { |
48 |
$args = array( |
49 |
'after' => '', |
50 |
'before' => '', |
51 |
'link_after' => '', |
52 |
'link_before' => '', |
53 |
'walker' => new Walker_Nav_Menu_Edit |
54 |
);
|
55 |
echo walk_nav_menu_tree( $menu_items, 0, (object) $args ); |
56 |
}
|
57 |
|
58 |
// Finally don't forget to exit
|
59 |
exit; |
60 |
}
|
Vayamos a través de esta devolución de llamada un poco a la vez. Primero verificamos los permisos del usuario, verificamos el nonce y cargamos la página nav-menu.php (necesitamos algunas de las funciones).
1 |
if ( ! current_user_can( 'edit_theme_options' ) ) |
2 |
die('-1'); |
3 |
|
4 |
check_ajax_referer('my-add-post-type-archive-links','posttypearchive_nonce'); |
5 |
|
6 |
require_once ABSPATH . 'wp-admin/includes/nav-menu.php'; |
7 |
|
8 |
if(empty($_POST['post_types'])) |
9 |
exit; |
Luego creamos un elemento de menú para cada tipo de publicación seleccionada. Primero verificamos que el tipo de publicación que recibimos existe verificando el valor devuelto por get_post_type_object(). Podemos obtener el enlace de archivo con la función get_post_type_archive_link()
Los elementos de menú son, de hecho, publicaciones del tipo de publicación 'nav_menu_item' con metadatos de publicación incorporados, incluidos los campos relacionados con 'url', 'type' y 'object'. El "type" del elemento es normalmente 'custom', 'post_type' o 'taxonomy' – pero estableceremos su valor en "post_type_archive". El valor meta del 'object' del elemento normalmente solo se usa para elementos 'post_type' o 'taxonomy' y se refiere al tipo de publicación o taxonomía al que se refiere el enlace. Usaremos esto para almacenar el tipo de publicación del enlace de archivo.
1 |
// Create menu items and store IDs in array
|
2 |
$item_ids=array(); |
3 |
foreach ( (array) $_POST['post_types'] as $post_type) { |
4 |
$post_type_obj = get_post_type_object($post_type); |
5 |
|
6 |
if(!$post_type_obj) |
7 |
continue; |
8 |
|
9 |
$menu_item_data= array( |
10 |
'menu-item-title' => esc_attr($post_type_obj->labels->name), |
11 |
'menu-item-type' => 'post_type_archive', |
12 |
'menu-item-object' => esc_attr($post_type), |
13 |
'menu-item-url' => get_post_type_archive_link($post_type) |
14 |
);
|
15 |
|
16 |
// Collect the items' IDs.
|
17 |
$item_ids[] = wp_update_nav_menu_item(0, 0, $menu_item_data ); |
18 |
}
|
19 |
|
20 |
// If there was an error die here
|
21 |
if ( is_wp_error( $item_ids ) ) |
22 |
die('-1'); |
A continuación simplemente generamos el HTML que se agregará al menú. Usamos la matriz $item_ids para obtener una variedad de elementos de menú y pasar esto a una clase de caminante de WordPress para hacer el trabajo duro por nosotros.
1 |
//Set up menu items
|
2 |
foreach ( (array) $item_ids as $menu_item_id ) { |
3 |
$menu_obj = get_post( $menu_item_id ); |
4 |
if ( ! empty( $menu_obj->ID ) ) { |
5 |
$menu_obj = wp_setup_nav_menu_item( $menu_obj ); |
6 |
$menu_obj->label = $menu_obj->title; // don't show "(pending)" in ajax-added items |
7 |
$menu_items[] = $menu_obj; |
8 |
}
|
9 |
}
|
10 |
|
11 |
//This gets the HTML to returns it to the menu
|
12 |
if ( ! empty( $menu_items ) ) { |
13 |
$args = array( |
14 |
'after' => '', |
15 |
'before' => '', |
16 |
'link_after' => '', |
17 |
'link_before' => '', |
18 |
'walker' => new Walker_Nav_Menu_Edit |
19 |
);
|
20 |
echo walk_nav_menu_tree( $menu_items, 0, (object) $args ); |
21 |
}
|
22 |
|
23 |
//Finally don't forget to exit
|
24 |
exit; |
Paso 6 El elemento del menú
Desafortunadamente, debido a un error con WordPress, si el tipo de su elemento no es 'taxonomy', 'custom' o 'post_type', la URL se elimina. Para contrarrestar esto, cuando se usa un enlace
'post_type_archive' en un menú, volvemos a agregar la URL manualmente.
Esto también garantiza que el enlace del archivo esté "actualizado" (en
caso de que se haya cambiado la estructura de enlace permanente).
1 |
public function setup_archive_item($menu_item){ |
2 |
if($menu_item->type !='post_type_archive') |
3 |
return $menu_item; |
4 |
|
5 |
$post_type = $menu_item->object; |
6 |
$menu_item->url =get_post_type_archive_link($post_type); |
7 |
|
8 |
return $menu_item; |
9 |
}
|
Paso 7 Haciendo el enlace actual
Finalmente, debemos hacer que el elemento sea "actual" cuando estemos en la página correspondiente. Quiero que el enlace de archivo de tipo de publicación se resalte como actual si estamos en esa página de archivo, o viendo una sola publicación de ese tipo. Para ello verifico:
Para actualizar el elemento, simplemente debemos agregar current-menu-item a las clases del elemento que se almacenan en $item->classes. Luego tenemos que recorrer sus padres en el menú y agregar las clases current_item_parent y current_item_ancestor. Veamos cada bit individualmente:
Recorremos cada uno de los elementos en el menú:
1 |
public function maybe_make_current($items) { |
2 |
foreach ($items as $item) { |
3 |
// This is where we check the item
|
4 |
}
|
5 |
return $items; |
6 |
}
|
Si el elemento no es de 'post_type_archive' o si lo es, pero no queremos que sea "actual", simplemente pasamos al siguiente elemento. Recuerde que para nuestros enlaces de archivo, el tipo de publicación se almacena como el objeto del elemento. Así que dentro del bucle foreach:
1 |
if('post_type_archive' != $item->type) |
2 |
continue; |
3 |
|
4 |
$post_type = $item->object; |
5 |
if(!is_post_type_archive($post_type)&& !is_singular($post_type)) |
6 |
continue; |
Si queremos actualizarlo, le asignamos la clase adecuada y luego seleccionamos a sus padres en el menú. Los elementos principales de un menú se almacenan como meta meta con la tecla meta _menu_item_menu_item_parent.
1 |
//Make item current
|
2 |
$item->current = true; |
3 |
$item->classes[] = 'current-menu-item'; |
4 |
|
5 |
//Get menu item's ancestors:
|
6 |
$_anc_id = (int) $item->db_id; |
7 |
$active_ancestor_item_ids=array(); |
8 |
|
9 |
while(( $_anc_id = get_post_meta( $_anc_id, '_menu_item_menu_item_parent', true ) ) && ! in_array( $_anc_id, $active_ancestor_item_ids ) ) { |
10 |
$active_ancestor_item_ids[] = $_anc_id; |
11 |
}
|
Luego repasamos los elementos del menú y damos las clases apropiadas a los padres y antepasados del elemento "current".
1 |
// Loop through the items and give ancestors and parents the appropriate class
|
2 |
foreach ($items as $key=>$parent_item) { |
3 |
$classes = (array) $parent_item->classes; |
4 |
|
5 |
// If menu item is the parent
|
6 |
if ($parent_item->db_id == $item->menu_item_parent ) { |
7 |
$classes[] = 'current-menu-parent'; |
8 |
$items[$key]->current_item_parent = true; |
9 |
}
|
10 |
|
11 |
// If menu item is an ancestor
|
12 |
if ( in_array( intval( $parent_item->db_id ), $active_ancestor_item_ids ) ) { |
13 |
$classes[] = 'current-menu-ancestor'; |
14 |
$items[$key]->current_item_ancestor = true; |
15 |
}
|
16 |
|
17 |
$items[$key]->classes = array_unique( $classes ); |
18 |
}
|
Poniendo esa función juntos:
1 |
public function maybe_make_current($items) { |
2 |
foreach ($items as $item) { |
3 |
if('post_type_archive' != $item->type) |
4 |
continue; |
5 |
|
6 |
$post_type = $item->object; |
7 |
if(!is_post_type_archive($post_type)&& !is_singular($post_type)) |
8 |
continue; |
9 |
|
10 |
// Make item current
|
11 |
$item->current = true; |
12 |
$item->classes[] = 'current-menu-item'; |
13 |
|
14 |
// Get menu item's ancestors:
|
15 |
$_anc_id = (int) $item->db_id; |
16 |
$active_ancestor_item_ids=array(); |
17 |
|
18 |
while(( $_anc_id = get_post_meta( $_anc_id, '_menu_item_menu_item_parent', true ) ) && ! in_array( $_anc_id, $active_ancestor_item_ids ) ) { |
19 |
$active_ancestor_item_ids[] = $_anc_id; |
20 |
}
|
21 |
|
22 |
// Loop through ancestors and give them 'ancestor' or 'parent' class
|
23 |
foreach ($items as $key=>$parent_item) { |
24 |
$classes = (array) $parent_item->classes; |
25 |
|
26 |
// If menu item is the parent
|
27 |
if ($parent_item->db_id == $item->menu_item_parent ) { |
28 |
$classes[] = 'current-menu-parent'; |
29 |
$items[$key]->current_item_parent = true; |
30 |
}
|
31 |
|
32 |
// If menu item is an ancestor
|
33 |
if ( in_array( intval( $parent_item->db_id ), $active_ancestor_item_ids ) ) { |
34 |
$classes[] = 'current-menu-ancestor'; |
35 |
$items[$key]->current_item_ancestor = true; |
36 |
}
|
37 |
|
38 |
$items[$key]->classes = array_unique( $classes ); |
39 |
}
|
40 |
|
41 |
}
|
42 |
return $items; |
43 |
}
|
Todo lo que queda es ir a la página de administración de sus complementos y activar el complemento.
Conclusión
Siempre hay espacio para mejorar. Por ejemplo, con un poco de jQuery puede agregar un enlace 'Seleccionar todo' debajo de las casillas de verificación o mostrar el símbolo 'cargando' mientras se procesa el AJAX. Ahora este complemento no es la solución más simple, pero funciona bien y evita los inconvenientes de simplemente agregar un enlace personalizado. El complemento anterior en su totalidad se puede encontrar en mi GitHub. Si tiene algún comentario o sugerencia, no dude en dejar un comentario o contactarme a través de Twitter.



