¿Cómo puedo colocar un meta box personalizado arriba del editor pero debajo de la sección del título en la página de edición de entrada?
Tengo un meta box personalizado para un tipo de contenido personalizado que mi cliente quiere colocar entre la sección del título/enlace permanente y el editor de entradas en el panel de administración. ¿Es esto posible y si es así, qué hook/filtro/etc necesitaría usar?

- Simplemente añade un meta box usando el contexto avanzado y prioridad alta
- Luego, engancha el hook
edit_form_after_title
Imprime tus meta boxes ahí y luego remuévelos para que no aparezcan dos veces.
// Mueve todos los metaboxes "avanzados" encima del editor por defecto add_action('edit_form_after_title', function() { global $post, $wp_meta_boxes; do_meta_boxes(get_current_screen(), 'advanced', $post); unset($wp_meta_boxes[get_post_type($post)]['advanced']); });

Un sitio en el que estoy trabajando registra algunos metaboxes usando el parámetro register_meta_box_cb
de la función register_post_type
. He probado tu código pero los metaboxes no se mueven encima del editor. ¿Se puede usar esto en mi caso? Gracias

Recomendaría usar un $context
personalizado, en lugar de advanced
, usar algo como my_before_editor
, para que no muevas todos los metaboxes en el contexto advanced
, sino que apuntes específicamente a tus metaboxes específicos... mira https://developer.wordpress.org/reference/functions/add_meta_box/

edit_form_after_title
no es compatible con el editor de bloques https://github.com/WordPress/gutenberg/issues/5821

A continuación te muestro cómo mover metaboxes específicos arriba del editor, pero antes de publicar el código me gustaría agradecer a Andrew y mhulse. ¡Ustedes son geniales!
function foo_deck( $post_type ) {
if ( in_array( $post_type, array( 'post', 'page' ) ) ) {
add_meta_box(
'contact_details_meta',
'Detalles de Contacto', // Traducido
'contact_details_meta',
$post_type,
'test', // cambiar a algo diferente de normal, advanced o side
'high'
);
}
}
add_action('add_meta_boxes', 'foo_deck');
function foo_move_deck() {
# Obtener las variables globales:
global $post, $wp_meta_boxes;
# Mostrar los metaboxes "avanzados":
do_meta_boxes( get_current_screen(), 'test', $post );
# Eliminar los metaboxes "avanzados" iniciales:
unset($wp_meta_boxes['post']['test']);
}
add_action('edit_form_after_title', 'foo_move_deck');

Para proporcionar un ejemplo de código completo basado en la respuesta de Andrew... Necesitaba una forma de incluir un "Deck" (también conocido como subtítulo) en mis publicaciones; quería que el campo del deck apareciera después de la barra de título principal.
/**
* Añade un meta box "deck" (subtítulo) a la página de edición de entradas y lo posiciona
* debajo del título.
*
* @todo Mover a una clase.
* @see http://codex.wordpress.org/Function_Reference/add_meta_box
* @see http://wordpress.org/extend/ideas/topic/add-meta-box-to-multiple-post-types
* @see https://github.com/Horttcore/WordPress-Subtitle
* @see http://codex.wordpress.org/Function_Reference/wp_nonce_field
*/
# Añade un cuadro a la columna principal en las pantallas de edición de Entradas y Páginas:
function foo_deck($post_type) {
# Tipos de entrada permitidos para mostrar el meta box:
$post_types = array('post', 'page');
if (in_array($post_type, $post_types)) {
# Añade un meta box a la interfaz administrativa:
add_meta_box(
'foo-deck-meta-box', // Atributo HTML 'id' de la sección de edición.
'Deck', // Título de la sección de edición, visible para el usuario.
'foo_deck_meta_box', // Función que imprime el HTML para la sección de edición.
$post_type, // Tipo de pantalla de edición donde mostrar la sección.
'advanced', // Parte de la página donde debe mostrarse la sección.
'high' // Prioridad dentro del contexto donde deben mostrarse los cuadros.
);
}
}
# Función de callback que imprime el contenido del cuadro:
function foo_deck_meta_box($post) {
# Usa `get_post_meta()` para recuperar un valor existente de la base de datos y usarlo en el formulario:
$deck = get_post_meta($post->ID, '_deck', true);
# Campo de formulario a mostrar:
?>
<label class="screen-reader-text" for="foo_deck">Deck</label>
<input id="foo_deck" type="text" autocomplete="off" value="<?=esc_attr($deck)?>" name="foo_deck" placeholder="Deck">
<?php
# Muestra el campo de formulario oculto nonce:
wp_nonce_field(
plugin_basename(__FILE__), // Nombre de la acción.
'foo_deck_meta_box' // Nombre del nonce.
);
}
/**
* @see https://wordpress.stackexchange.com/a/16267/32387
*/
# Guarda nuestros datos personalizados cuando se guarda la entrada:
function foo_deck_save_postdata($post_id) {
# ¿Está autorizado el usuario actual para realizar esta acción?
if ((($_POST['post_type'] === 'page') && current_user_can('edit_page', $post_id) || current_user_can('edit_post', $post_id))) { // Si es una página, O, si es una entrada, ¿puede el usuario editarla?
# Evita que WP borre los campos personalizados en autoguardado:
if ((( ! defined('DOING_AUTOSAVE')) || ( ! DOING_AUTOSAVE)) && (( ! defined('DOING_AJAX')) || ( ! DOING_AJAX))) {
# Verificación del nonce:
if (wp_verify_nonce($_POST['foo_deck_meta_box'], plugin_basename(__FILE__))) {
# Obtiene el deck enviado:
$deck = sanitize_text_field($_POST['foo_deck']);
# ¿Añadir, actualizar o eliminar?
if ($deck !== '') {
# El deck existe, así que lo añade O actualiza:
add_post_meta($post_id, '_deck', $deck, true) OR update_post_meta($post_id, '_deck', $deck);
} else {
# El deck está vacío o se eliminó:
delete_post_meta($post_id, '_deck');
}
}
}
}
}
# Obtiene el deck:
function foo_get_deck($post_id = FALSE) {
$post_id = ($post_id) ? $post_id : get_the_ID();
return apply_filters('foo_the_deck', get_post_meta($post_id, '_deck', TRUE));
}
# Muestra el deck (se sentirá mejor cuando sea OOP):
function foo_the_deck() {
echo foo_get_deck(get_the_ID());
}
# Comprobador condicional:
function foo_has_subtitle($post_id = FALSE) {
if (foo_get_deck($post_id)) return TRUE;
}
# Define el cuadro personalizado:
add_action('add_meta_boxes', 'foo_deck');
# Haz algo con los datos introducidos:
add_action('save_post', 'foo_deck_save_postdata');
/**
* @see https://wordpress.stackexchange.com/questions/36600
* @see https://wordpress.stackexchange.com/questions/94530/
*/
# Ahora mueve los meta boxes avanzados después del título:
function foo_move_deck() {
# Obtiene las variables globales:
global $post, $wp_meta_boxes;
# Muestra los meta boxes "avanzados":
do_meta_boxes(get_current_screen(), 'advanced', $post);
# Elimina los meta boxes "avanzados" iniciales:
unset($wp_meta_boxes['post']['advanced']);
}
add_action('edit_form_after_title', 'foo_move_deck');
Obviamente, el código anterior podría mejorarse, pero debería ayudar a otros que intenten hacer lo mismo (la respuesta de Andrew arrojó luz, pero pensé que podría ser útil proporcionar un ejemplo funcional).
Mejoras que podrían hacerse:
- Convertir a OOP/clase(s).
- Añadir estilos/js para que se vea/sienta/comporte como el campo de título.
Planeo hacer las mejoras anteriores en algún momento en el futuro, pero al menos el código anterior debería ayudar a otros que intenten resolver esto.
Consulta el código fuente aquí para más inspiración (ellos optaron por usar jQuery para mover el "subtítulo").

Por si ayuda a alguien que esté recorriendo el mismo camino: Hice una pregunta aquí que tiene algo de código relacionado/similar (opté por usar el campo "title" para almacenar y filtrar el subtítulo).

En lugar de mover todo en la sección avanzada hacia arriba, ¿por qué no crear una nueva sección y moverla a la parte superior?:
// Crear sección 'top' y moverla a la parte superior
add_action('edit_form_after_title', function() {
global $post, $wp_meta_boxes;
do_meta_boxes(get_current_screen(), 'top', $post);
unset($wp_meta_boxes[get_post_type($post)]['top']);
});
Ahora todo lo que necesitas hacer es registrar el meta box usando top
para la sección y high
para la prioridad.
Está funcionando en WordPress 4.4.2 para mí. No he probado esto en otras versiones de WP.

Hay otra forma, por cierto podemos colocar el editor en cualquier posición:
Eliminar el editor del parámetro support al registrar el post_type
Añadir un metabox falso
add_meta_box( 'no-importa', __( 'Descripción'), function($post){ wp_editor($post->post_content,'post_content',array('name'=>'post_content')); }, 'post_type_type', 'advanced', 'high' );
