Как создать/обновить XML карту сайта без плагинов?
Я предпочитаю жестко кодировать все на моих WordPress сайтах без использования плагинов. Есть ли способ генерировать или обновлять карту сайта каждый раз, когда я публикую/обновляю запись на одном из моих блогов Multisite, не используя плагины?

Следующий код работает сразу после установки. Ваша карта сайта будет доступна по адресу: https://your-website-name.com/sitemap.xml
Каждый раз при создании или обновлении страницы, записи или пользовательского типа записи она будет обновляться. Убедитесь, что вы указали название вашего пользовательского типа записи:
add_action( 'publish_post', 'ow_create_sitemap' );
add_action( 'publish_page', 'ow_create_sitemap' );
add_action( 'save_post', 'ow_create_sitemap' );
function ow_create_sitemap() {
$postsForSitemap = get_posts(array(
'numberposts' => -1,
'orderby' => 'modified',
// 'custom_post' следует заменить на ваш собственный Пользовательский Тип Записи (один или несколько)
'post_type' => array( 'post', 'page', 'custom_post' ),
'order' => 'DESC'
));
$sitemap = '<?xml version="1.0" encoding="UTF-8"?>';
$sitemap .= '<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.sitemaps.org/schemas/sitemap/0.9 http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd">';
foreach( $postsForSitemap as $post ) {
setup_postdata( $post );
$postdate = explode( " ", $post->post_modified );
$sitemap .= '<url>'.
'<loc>' . get_permalink( $post->ID ) . '</loc>' .
'<lastmod>' . $postdate[0] . '</lastmod>' .
'<changefreq>monthly</changefreq>' .
'</url>';
}
$sitemap .= '</urlset>';
$fp = fopen( ABSPATH . 'sitemap.xml', 'w' );
fwrite( $fp, $sitemap );
fclose( $fp );
}

Я не знаю, работает ли это на мультисайтах, но в одиночной установке WordPress у меня это работает идеально.
Когда вы создаёте/обновляете записи или страницы, это генерирует файл sitemap.xml и обновляет ссылки (URL), располагая самые свежие первыми (последние изменённые).
Скопируйте и вставьте приведённый ниже код в файл functions.php вашей активной темы:
/* Функция для создания файла sitemap.xml в корневой директории сайта */
// add_action("publish_post", "eg_create_sitemap");
// add_action("publish_page", "eg_create_sitemap");
add_action( "save_post", "eg_create_sitemap" );
function eg_create_sitemap() {
$postsForSitemap = get_posts( array(
'numberposts' => -1,
'orderby' => 'modified',
'post_type' => array( 'post', 'page' ),
'order' => 'DESC'
) );
$sitemap = '<?xml version="1.0" encoding="UTF-8"?>';
$sitemap .= "\n" . '<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">' . "\n";
foreach( $postsForSitemap as $post ) {
setup_postdata( $post );
$postdate = explode( " ", $post->post_modified );
$sitemap .= "\t" . '<url>' . "\n" .
"\t\t" . '<loc>' . get_permalink( $post->ID ) . '</loc>' .
"\n\t\t" . '<lastmod>' . $postdate[0] . '</lastmod>' .
"\n\t\t" . '<changefreq>monthly</changefreq>' .
"\n\t" . '</url>' . "\n";
}
$sitemap .= '</urlset>';
$fp = fopen( ABSPATH . "sitemap.xml", 'w' );
fwrite( $fp, $sitemap );
fclose( $fp );
}

Я не думаю, что это будет работать на мультисайте - он записывает sitemap.xml в то же самое расположение файла на сервере, так что будет только один фактический файл sitemap.xml, который будет перезаписываться каждый раз, когда какой-либо блог вносит изменения...

Запрос всех записей на сайте при каждом сохранении - верный способ обрушить сайт, который имеет нетривиальное количество контента. Кроме того, это будет делать сохранение новых записей всё медленнее и медленнее с каждой опубликованной записью.

@MarkKaplun - Что бы вы порекомендовали в качестве альтернативы запросу всех записей?

Перед использованием кода, предоставленного в ответе w3uiguru, мне пришлось внести некоторые улучшения, соответствующие общепринятым стандартам для XML-файлов. Код приведен ниже:
/* функция для создания файла sitemap.xml в корневой директории сайта */
// add_action("publish_post", "eg_create_sitemap");
// add_action("publish_page", "eg_create_sitemap");
add_action( "save_post", "eg_create_sitemap" );
function eg_create_sitemap() {
if ( str_replace( '-', '', get_option( 'gmt_offset' ) ) < 10 ) {
$tempo = '-0' . str_replace( '-', '', get_option( 'gmt_offset' ) );
} else {
$tempo = get_option( 'gmt_offset' );
}
if( strlen( $tempo ) == 3 ) { $tempo = $tempo . ':00'; }
$postsForSitemap = get_posts( array(
'numberposts' => -1,
'orderby' => 'modified',
'post_type' => array( 'post', 'page' ),
'order' => 'DESC'
) );
$sitemap .= '<?xml version="1.0" encoding="UTF-8"?>' . '<?xml-stylesheet type="text/xsl" href="' .
esc_url( home_url( '/' ) ) . 'sitemap.xsl"?>';
$sitemap .= "\n" . '<urlset xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.sitemaps.org/schemas/sitemap/0.9 http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd" xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">' . "\n";
$sitemap .= "\t" . '<url>' . "\n" .
"\t\t" . '<loc>' . esc_url( home_url( '/' ) ) . '</loc>' .
"\n\t\t" . '<lastmod>' . date( "Y-m-d\TH:i:s", current_time( 'timestamp', 0 ) ) . $tempo . '</lastmod>' .
"\n\t\t" . '<changefreq>daily</changefreq>' .
"\n\t\t" . '<priority>1.0</priority>' .
"\n\t" . '</url>' . "\n";
foreach( $postsForSitemap as $post ) {
setup_postdata( $post);
$postdate = explode( " ", $post->post_modified );
$sitemap .= "\t" . '<url>' . "\n" .
"\t\t" . '<loc>' . get_permalink( $post->ID ) . '</loc>' .
"\n\t\t" . '<lastmod>' . $postdate[0] . 'T' . $postdate[1] . $tempo . '</lastmod>' .
"\n\t\t" . '<changefreq>Weekly</changefreq>' .
"\n\t\t" . '<priority>0.5</priority>' .
"\n\t" . '</url>' . "\n";
}
$sitemap .= '</urlset>';
$fp = fopen( ABSPATH . "sitemap.xml", 'w' );
fwrite( $fp, $sitemap );
fclose( $fp );
}

Не могли бы вы объяснить ваш код и отправить [правку] для улучшения его читаемости? Спасибо

@locutor-antonio-cezar @gabriel Вам нужно изменить Weekly
на weekly
из-за Error 1840: Element '{http://www.sitemaps.org/schemas/sitemap/0.9}changefreq': [facet 'enumeration'] The value 'Weekly' is not an element of the set {'always', 'hourly', 'daily', 'weekly', 'monthly', 'yearly', 'never'}
.

Начиная с версии 5.5, WordPress имеет встроенную функцию XML-карты сайта, которую можно настраивать.
Подробности и несколько быстрых примеров можно найти в этой записи блога. Ознакомьтесь здесь со списком хуков, которые можно использовать для настройки стандартной карты сайта.

Я немного изменил код от @locutor-antonio-cezar, так как мне нужен был очень специфический вариант использования. Мне требовалась карта сайта, специально адаптированная для Google News. В чем отличие? Вся разметка соответствует правилам. В моем конкретном случае я ограничил количество записей до 20. Также записи старше 2 дней исчезают из карты сайта. Возможно, кому-то это пригодится:
/* функция для создания файла sitemap.xml в корневой директории сайта */
// add_action("publish_post", "eg_create_sitemap");
// add_action("publish_page", "eg_create_sitemap");
add_action( "save_post", "eg_create_sitemap" );
function eg_create_sitemap() {
if ( str_replace( '-', '', get_option( 'gmt_offset' ) ) < 10 ) {
$tempo = '-0' . str_replace( '-', '', get_option( 'gmt_offset' ) );
} else {
$tempo = get_option( 'gmt_offset' );
}
if( strlen( $tempo ) == 3 ) { $tempo = $tempo . ':00'; }
$postsForSitemap = get_posts( array(
'numberposts' => 20,
'orderby' => 'modified',
'post_type' => array( 'post', 'page' ),
'order' => 'DESC',
'date_query' => array(
'after' => date('Y-m-d', strtotime('-2 days'))
)
) );
$sitemap .= '<?xml version="1.0" encoding="UTF-8"?>';
$sitemap .= "\n" . '<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" xmlns:news="http://www.google.com/schemas/sitemap-news/0.9" xmlns:xhtml="http://www.w3.org/1999/xhtml" xmlns:mobile="http://www.google.com/schemas/sitemap-mobile/1.0" xmlns:image="http://www.google.com/schemas/sitemap-image/1.1" xmlns:video="http://www.google.com/schemas/sitemap-video/1.1">' . "\n";
foreach( $postsForSitemap as $post ) {
setup_postdata( $post);
$postdate = explode( " ", $post->post_modified );
$sitemap .= "\t" . "<url>" . "\n";
$sitemap .= "\t\t" . "<loc>" . get_permalink( $post->ID ) . '</loc>';
$sitemap .= "\t\t" . '<news:news>' . "\n";
$sitemap .= "\t\t\t" . '<news:publication>' . "\n";
$sitemap .= "\t\t\t\t" . '<news:name><![CDATA[ ВАШ САЙТ ]]></news:name>' . "\n";
$sitemap .= "\t\t\t\t" . '<news:language>ВАШ ЯЗЫК</news:language>' . "\n";
$sitemap .= "\t\t\t" . '</news:publication>' . "\n";
$sitemap .= "\t\t\t<news:publication_date>" . $postdate[0] . 'T' . $postdate[1] . $tempo . "</news:publication_date>\n";
$sitemap .= "\t\t\t" . '<news:title><![CDATA[' . get_the_title( $post) . ']]></news:title>' . "\n";
$sitemap .= "\t\t" . '</news:news>' . "\n";
$sitemap .= "\t" . '</url>' . "\n";
}
$sitemap .= '</urlset>';
$fp = fopen( ABSPATH . "sitemap_news.xml", 'w' );
fwrite( $fp, $sitemap );
fclose( $fp );
}
