Шорткод выводится в начале the_content
Шорткод, созданный этой функцией (список всех сайтов в мультисайте), выводится в начале контента в цикле, независимо от его расположения в редакторе.
Я изучил похожие вопросы и ответы на WPSE и понимаю, что проблема связана с использованием echo
вместо return
в функции, но просто заменить echo
на return
в функции ниже недостаточно. Или добавить echo=0
с функцией WP типа wp_list_pages()
.
Есть идеи? Где именно функция должна возвращать значение вместо вывода?
// Вывод одного элемента меню
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) != '/') {
// Примечание: я добавил "/" в конец URL, потому что WordPress
// не делал этого автоматически в версии 3.0.4
$url .= '/';
}
echo '<a href="' . $url . '">' . $title . '</a>';
if ($id == $blog_id) {
echo '</strong>';
}
echo '</li>';
}
}
// Вывод всего меню
// Если $link_self false, пропускаем текущий сайт - используется для отображения меню на главной странице
function projects_menu($link_self = true)
{
global $wpdb;
echo '<ul>';
projects_menu_entry(1, 'Главная', $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>';
}
// Добавляем шорткод [bloglist]
function bloglist_shortcode($atts)
{
projects_menu(false);
}
add_shortcode('bloglist', 'bloglist_shortcode');

Все функции должны возвращать строку, не следует использовать echo
где-либо. Перепишите функции, используя внутреннюю переменную для работы со строками и возвращайте её:
// Вывод отдельного пункта меню
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;
}
// Вывод всего меню
// Если $link_self равен false, пропускаем текущий сайт - используется для отображения меню на главной странице
function projects_menu($link_self = true)
{
global $wpdb;
$out = '<ul>';
$out .= projects_menu_entry(1, 'Главная', $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;
}
// Добавляет шорткод [bloglist]
function bloglist_shortcode($atts)
{
return projects_menu(false);
}
add_shortcode('bloglist', 'bloglist_shortcode');
Для похожего, расширенного примера см.: Как возвращать содержимое цикла.

Вы не можете заменить экземпляры echo
, потому что можете вернуть значение только один раз. Вам нужно собрать строку и вернуть её.
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>';
}
// и так далее
$ret .= '</ul>';
return $ret;
}
Сделайте это для обеих функций, и я ожидаю, что это заработает. Извините, если я что-то кардинально неправильно понял.

Есть идеи? Где функция, которая должна возвращать значение вместо вывода через echo?
Я предлагаю альтернативное решение, которое не требует замены экземпляров echo или построения возвращаемой строки.
Вы можете включить буферизацию вывода и вернуть буфер в виде строки.
Добавьте ob_start()
в начало функции перед любым вызовом echo.
В конце функции добавьте:
$response = ob_get_contents();
ob_end_clean();
return $response;
