El shortcode se muestra en la parte superior del the_content
El shortcode producido por esta función - una lista de todos los sitios en una red multisitio - se muestra arriba del contenido en el loop, sin importar dónde se coloque en el editor.
He revisado otras preguntas y respuestas relacionadas en WPSE y entiendo que tiene que ver con que la función usa echo
en lugar de return
, pero no es tan simple como reemplazar las instancias de echo
con return
en la función de abajo. O añadir echo=0
con una función de WP como wp_list_pages()
¿Alguna idea? ¿Dónde está la función que necesita devolver un valor en lugar de hacer echo?
// Mostrar un elemento individual del menú
function projects_menu_entry($id, $title, $link_self)
{
global $blog_id;
if ($link_self || $id != $blog_id) {
echo '<li>';
if ($id == $blog_id) {
echo '<strong>';
}
$url = get_home_url($id);
if (substr($url, -1) != '/') {
// Nota: Añadí un "/" al final de la URL porque WordPress
// no lo hacía automáticamente en la versión 3.0.4
$url .= '/';
}
echo '<a href="' . $url . '">' . $title . '</a>';
if ($id == $blog_id) {
echo '</strong>';
}
echo '</li>';
}
}
// Mostrar el menú completo
// Si $link_self es false, omite el sitio actual - usado para mostrar el menú en la página principal
function projects_menu($link_self = true)
{
global $wpdb;
echo '<ul>';
projects_menu_entry(1, 'Home', $link_self);
$blogs = $wpdb->get_results("
SELECT blog_id
FROM {$wpdb->blogs}
WHERE site_id = '{$wpdb->siteid}'
AND spam = '0'
AND deleted = '0'
AND archived = '0'
AND blog_id != 1
");
$sites = array();
foreach ($blogs as $blog) {
$sites[$blog->blog_id] = get_blog_option($blog->blog_id, 'blogname');
}
natsort($sites);
foreach ($sites as $blog_id => $blog_title) {
projects_menu_entry($blog_id, $blog_title, $link_self);
}
echo '</ul>';
}
// Añade un shortcode [bloglist]
function bloglist_shortcode($atts)
{
projects_menu(false);
}
add_shortcode('bloglist', 'bloglist_shortcode');

Todas las funciones deben retornar una cadena, no deberías usar echo
en ningún lugar. Reescribe las funciones, usa una variable interna para manejar las cadenas y retorna esa:
// Mostrar un solo elemento del menú
function projects_menu_entry($id, $title, $link_self)
{
global $blog_id;
$out = '';
if ($link_self || $id != $blog_id) {
$out .= '<li>';
if ($id == $blog_id) {
$out .= '<strong>';
}
$url = get_home_url( $id, '/' );
$out .= '<a href="' . $url . '">' . $title . '</a>';
if ($id == $blog_id) {
$out .= '</strong>';
}
$out .= '</li>';
}
return $out;
}
// Mostrar el menú completo
// Si $link_self es falso, omite el sitio actual - usado para mostrar el menú en la página de inicio
function projects_menu($link_self = true)
{
global $wpdb;
$out = '<ul>';
$out .= projects_menu_entry(1, 'Inicio', $link_self);
$blogs = $wpdb->get_results("
SELECT blog_id
FROM {$wpdb->blogs}
WHERE site_id = '{$wpdb->siteid}'
AND spam = '0'
AND deleted = '0'
AND archived = '0'
AND blog_id != 1
");
$sites = array();
foreach ($blogs as $blog) {
$sites[$blog->blog_id] = get_blog_option($blog->blog_id, 'blogname');
}
natsort($sites);
foreach ($sites as $blog_id => $blog_title) {
$out .= projects_menu_entry($blog_id, $blog_title, $link_self);
}
$out .= '</ul>';
return $out;
}
// Añade un shortcode [listadeblogs]
function bloglist_shortcode($atts)
{
return projects_menu(false);
}
add_shortcode('listadeblogs', 'bloglist_shortcode');
Para un ejemplo similar ampliado ver: Cómo retornar contenidos del loop.

No puedes reemplazar las instancias de echo
porque solo puedes retornar una vez. Necesitas construir una cadena y retornarla.
function projects_menu_entry($id, $title, $link_self)
{
global $blog_id;
$ret = '';
if ($link_self || $id != $blog_id) {
$ret .= '<li>';
if ($id == $blog_id) {
$ret .= '<strong>';
}
// y así sucesivamente
$ret .= '</ul>';
return $ret;
}
Haz eso para ambas funciones y esperaría que esto funcione. Mis disculpas si he leído algo incorrectamente.

¿Alguna idea? ¿Dónde está la función que necesita devolverse en lugar de hacer echo?
Voy a ofrecer una solución alternativa que no requiere que reemplaces instancias de echo o construyas una cadena de retorno.
Puedes activar el almacenamiento en búfer de salida y devolver el búfer como una cadena.
Añade ob_start()
al principio de la función antes de cualquier llamada a echo.
Al final de la función añade:
$response = ob_get_contents();
ob_end_clean();
return $response;
