add_menu_page() con nombre diferente para el primer elemento del submenú
La documentación de add_menu_page
indica que se debe pasar el título del menú como segundo parámetro:
add_menu_page('Título de Página', 'Título del Menú', ...);
Cuando se agregan más páginas posteriormente mediante add_submenu_page
, la página principal se convierte en la primera entrada en el submenú:
Sin embargo, quiero que el primer elemento de la lista tenga un nombre diferente (pero que siga apuntando a la misma página), de la manera en que WordPress lo hace:
¿Cómo podría lograr esto en mi plugin?

Puedes hacer que el 'slug' de la página del submenú sea igual al de la página de nivel superior, y ambos apuntarán al mismo lugar:
add_action('admin_menu', 'my_menu_pages');
function my_menu_pages(){
add_menu_page('My Page Title', 'My Menu Title', 'manage_options', 'my-menu', 'my_menu_output' );
add_submenu_page('my-menu', 'Submenu Page Title', 'Whatever You Want', 'manage_options', 'my-menu' );
add_submenu_page('my-menu', 'Submenu Page Title2', 'Whatever You Want2', 'manage_options', 'my-menu2' );
}
Ejemplo:

Esto resultaría en entradas duplicadas, ya que Wordpress automáticamente crea una subpágina de menú para la página principal.

Recuerdo haber revisado por encima el código central la semana pasada y encontrarme con una nota que decía algo como: "Añadir elemento de submenú por defecto SI EL USUARIO NO LO TIENE YA". Comprueba si hay un elemento de submenú que apunte al elemento de nivel superior. Si lo hay, no añade el predeterminado.

Quiero que cuando cree un menú personalizado pueda mostrar algunas tablas y datos como otras pestañas muestran un enlace de tutorial o alguna función. Sería de ayuda cualquier sugerencia.

Esto funciona perfectamente. Asegúrate de tener al menos un submenú además del primero, ya que el primero apunta al slug principal. Si no, tus submenús no serán visibles.

Obviamente sigue funcionando en WP 5.9. Como la función de callback para add_menu_page es opcional, la eliminé y simplemente me aseguré de que el slug para el menú y el primer submenú sean iguales. Como dijo @MichaelLewis, esto resulta en eliminar el primer submenú predeterminado/redundante. ¡Bravo!

Hacer que el slug del elemento del menú padre y del submenú sean iguales (el primer elemento) como se muestra a continuación
function actions_recent_bids_add_admin_page(){
add_menu_page(
'Ofertas Recientes', // Título de la página
'Reportes de Subastas', // Título del menú
'manage_options', // Capacidad requerida
'wc-auction-reports', // Slug
'actions_recent_bids_list', // Función de callback
'dashicons-chart-area', // Icono
56 // Posición
);
add_submenu_page(
'wc-auction-reports', // Slug padre
'Ofertas Recientes', // Título de la página
'Ofertas Recientes', // Título del menú
'manage_options', // Capacidad requerida
'wc-auction-reports', // Slug
'acutions_customers_spendings_list' // Función de callback
);
add_submenu_page(
'wc-auction-reports', // Slug padre
'Gastos de Clientes', // Título de la página
'Gastos de Clientes', // Título del menú
'manage_options', // Capacidad requerida
'wc-acutions-customers-spendings', // Slug
'acutions_customers_spendings_list' // Función de callback
);
add_submenu_page(
'wc-auction-reports', // Slug padre
'Ofertas de Clientes', // Título de la página
'Ofertas de Clientes', // Título del menú
'manage_options', // Capacidad requerida
'wc-acutions-customers-bids', // Slug
'acutions_customers_bids_list' // Función de callback
);
}
add_action('admin_menu','actions_recent_bids_add_admin_page');

Hola, acabo de pasar una eternidad buscando esto y la forma correcta no está listada aquí. Lo que necesitas usar es:
remove_submenu_page('parent_slug','parent_slug');
al final de tu función
Ejemplo:
function posts_sync_menu() {
// Elemento principal del menú (actúa como contenedor)
add_menu_page(
'Mis Publicaciones', // Título de la página
'Mis Publicaciones', // Título del menú
'manage_options', // Capacidad requerida
'my-posts', // Slug del menú (necesario solo para inicialización)
'', // Sin función/callback necesaria
'dashicons-networking', // Icono
6 // Posición
);
add_submenu_page(
'my-posts', // Slug del padre
'Listado de Mis Publicaciones', // Título de la página
'Listado de Mis Publicaciones', // Título del menú
'manage_options', // Capacidad requerida
'my-posts-listing', // Slug
'my_posts_callback' // Callback para renderizar la página
);
// Eliminar la entrada del submenú padre y tomar el slug del "Listado de Mis Publicaciones"
remove_submenu_page('my-posts','my-posts');
}
Renderizará esto:
Básicamente tendrán el mismo slug con diferentes nombres de menú, tal como se solicitó.

Pero la pregunta es sobre agregar un menú duplicado, ¿no sobre eliminar uno?

Podría lograrse eliminando la entrada original y luego añadiendo una nueva con un nombre diferente pero el mismo slug. ¡Solo una idea!

¡¡¡Actualización!!! Si no estás seguro de si el menú padre existe, primero puedes verificarlo de la siguiente manera:
function contacts_page_submenu(){
// Verificar si el menú existe (si el Padre existe), no lo necesitamos pero él escribió :| (soy el editor)
if(menu_page_url('contacts-parent-menu', false )){
// Cambiar el texto del primer submenú
add_submenu_page(
'contacts-parent-menu', // Slug del Padre requerido
'Configuración de Contactos',
'Configuración de Contactos',
'manage_options',
'contacts-parent-menu', //Slug del Padre requerido
'contacts_submenu_callback' // Nombre de la función de callback del Padre
);
}
}
add_action('admin_menu', 'contacts_page_submenu');
Probado y funciona en WordPress 6.
Referencia: https://developer.wordpress.org/reference/functions/add_submenu_page/

function content_adder_menu() {
add_menu_page(
'Contenido', // Título de la página
'Contenido', // Título del menú
'manage_options', // Capacidad requerida para acceder
'content-adder', // Slug del menú
'content_adder_page' // Función de callback para mostrar la página
);
// Agregar una página de submenú bajo el menú principal
add_submenu_page(
'content-adder', // Slug del menú padre
'Agregar Contenido', // Título de la página
'Agregar Contenido', // Título del menú
'manage_options', // Capacidad requerida para acceder
'add-content', // Slug del menú
'add_content_page' // Función de callback para mostrar la página
);
}
// Función de callback para la página del menú principal
function content_adder_page() {
// Mostrar el contenido de la página del menú principal aquí
echo '<div class="wrap">';
echo '<h1>Bienvenido a Contenido</h1>';
// Obtener publicaciones del tipo de publicación 'post'
$args = array(
'post_type' => 'post', // Ajustar el tipo de publicación según sea necesario
'post_status' => 'publish', // Obtener solo publicaciones publicadas
'posts_per_page' => -1, // Obtener todas las publicaciones
);
$query = new WP_Query($args);
if ($query->have_posts()) {
echo '<h2>Lista de Publicaciones</h2>';
echo '<ul>';
while ($query->have_posts()) {
$query->the_post();
echo '<li><a href="' . get_permalink() . '">' . get_the_title() . '</a></li>';
}
echo '</ul>';
wp_reset_postdata(); // Restaurar los datos globales de la publicación
} else {
echo '<p>No se encontraron publicaciones.</p>';
}
echo '</div>';
}
// Función de callback para la página de submenú
function add_content_page() {
// Mostrar el contenido de la página de submenú aquí
echo '<div class="wrap">';
echo '<h1>Agregar Contenido </h1>';
// Verificar si se envió el formulario
if(isset($_POST['submit'])) {
// Procesar los datos del formulario aquí
$url = sanitize_text_field($_POST['url']);
$content = wp_kses_post($_POST['content']);
// Guardar o usar los datos enviados según sea necesario
// Por ejemplo, puedes guardarlos en un tipo de publicación personalizado o mostrarlos
$new_post = array(
'post_title' => 'Título de tu publicación',
'post_content' => $content, // El contenido que deseas guardar
'post_status' => 'publish', // Publicar la publicación
'post_author' => 1, // ID del autor (1 es típicamente el administrador)
'post_type' => 'post', // Tipo de publicación (cambiar si es necesario)
);
$post_id = wp_insert_post($new_post);
if ($post_id) {
// La publicación se insertó correctamente
echo 'ID de publicación ' . $post_id . ' creada.';
} else {
// Hubo un error
echo 'Error al crear la publicación.';
}
echo "<p>URL: $url</p>";
echo "<div>Contenido: $content</div>";
} else {
// Mostrar el formulario
echo '<form method="post">';
echo '<label for="url">Agregar URL:</label> <br>';
echo '<input type="text" name="url" id="url" /><br />';
echo '<label for="content">Agregar Contenido:</label> <br>';
echo '<textarea name="content" id="content" rows="5" cols="40"></textarea><br />';
echo '<input type="submit" name="submit" value="Agregar Contenido" class="button button-primary" />';
echo '</form>';
}
echo '</div>';
}
add_action('admin_menu', 'content_adder_menu');

add_submenu_page(
'tut_theme_settings', // slug del menú padre
'Elementos de la Página Principal 2', // título de la página
'Página Principal 2', // título del menú
'manage_options', // capacidad requerida
'tut_theme_settings2', // slug
'theme_front_page_settings' // función de callback
);
si el nombre del primer sub-menú es diferente, crear el mismo slug para el padre y el primer hijo y llamar a la misma función
