Files
profile/inc/article-rss-feed.php
Profile Profile ed4d79b706 add files inc
2026-03-09 20:51:08 +03:00

271 lines
10 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
/**
* Custom RSS Feed for profile_article posts
*/
// Защита от прямого доступа
if (!defined('ABSPATH')) {
exit;
}
class CustomProfileArticleRSS {
public function __construct() {
add_action('init', [$this, 'add_rss_endpoint']);
add_action('template_redirect', [$this, 'generate_rss_feed']);
}
/**
* Добавляем endpoint для RSS ленты
*/
public function add_rss_endpoint() {
add_rewrite_rule(
'^rss-feed/?$',
'index.php?custom_rss_feed=1',
'top'
);
add_rewrite_tag('%custom_rss_feed%', '([^&]+)');
add_rewrite_tag('%rss_date%', '([^&]+)');
}
/**
* Генерируем RSS ленту
*/
public function generate_rss_feed() {
if (get_query_var('custom_rss_feed') || isset($_GET['rss-feed'])) {
$date = isset($_GET['date']) ? sanitize_text_field($_GET['date']) : date('Y-m-d');
// Валидация даты
if (!preg_match('/^\d{4}-\d{2}-\d{2}$/', $date)) {
$date = date('Y-m-d');
}
$this->output_rss_feed($date);
exit;
}
}
/**
* Получаем URL иконки сайта
*/
private function get_site_icon_url() {
$site_icon_id = get_option('site_icon');
if ($site_icon_id) {
return wp_get_attachment_image_url($site_icon_id, 'full');
}
// Fallback на логотип или дефолтную иконку
$custom_logo_id = get_theme_mod('custom_logo');
if ($custom_logo_id) {
return wp_get_attachment_image_url($custom_logo_id, 'full');
}
return home_url('/wp-admin/images/wordpress-logo.png');
}
private function get_clean_post_content($post) {
$content = apply_filters('the_content', $post->post_content);
// Удаляем запрещенные для Турбо элементы
$content = preg_replace('/<script\b[^>]*>.*?<\/script>/is', '', $content);
$content = preg_replace('/<style\b[^>]*>.*?<\/style>/is', '', $content);
$content = preg_replace('/<iframe\b[^>]*>.*?<\/iframe>/is', '', $content);
$content = preg_replace('/<form\b[^>]*>.*?<\/form>/is', '', $content);
$content = preg_replace('/<input[^>]*>/is', '', $content);
$content = preg_replace('/<button[^>]*>.*?<\/button>/is', '', $content);
$content = preg_replace('/<select[^>]*>.*?<\/select>/is', '', $content);
$content = preg_replace('/<textarea[^>]*>.*?<\/textarea>/is', '', $content);
$content = str_replace('<p></p>', '', $content);
// Удаляем определенные классы
$content = preg_replace('/<div[^>]*class="[^"]*(ads|advert|banner|social|share|widget|comments)[^"]*"[^>]*>.*?<\/div>/is', '', $content);
// Разрешаем только турбо-совместимые теги
$allowed_tags = [
'p' => ['class' => [], 'style' => []],
'br' => [],
'strong' => [],
'em' => [],
'b' => [],
'i' => [],
'ul' => [],
'ol' => [],
'li' => [],
'h1' => [],
'h2' => [],
'h3' => [],
'h4' => [],
'h5' => [],
'h6' => [],
'a' => ['href' => [], 'title' => []],
'img' => ['src' => [], 'alt' => [], 'title' => [], 'width' => [], 'height' => []],
'blockquote' => [],
'figure' => [],
'header' => [],
];
$content = wp_kses($content, $allowed_tags);
return $content;
}
/**
* Формируем XML ленту
*/
private function output_rss_feed($date) {
global $wpdb;
// Определяем диапазон дат: 7 дней до указанной даты
$end_date = $date; // указанная дата
$start_date = date('Y-m-d', strtotime($date . ' -6 days')); // 7 дней назад (включая указанную дату)
//такая лента формируется на каждую дату и содержит все статьи за 7 дней до указанной даты
$posts = $wpdb->get_results($wpdb->prepare("
SELECT *
FROM {$wpdb->posts}
WHERE post_type = 'profile_article'
AND post_status = 'publish'
AND DATE(post_date) BETWEEN %s AND %s
AND post_password = ''
AND ID NOT IN (
SELECT post_id
FROM {$wpdb->postmeta}
WHERE meta_key IN ('_no_aeroflot', '_is_advertisement', '_only_link_access', '_hide_on_website', '_hide_on_mainpage')
AND meta_value = '1'
)
ORDER BY post_date DESC
", $start_date, $end_date));
// Конвертируем в объекты WP_Post
$posts = array_map(function($post) {
return new WP_Post($post);
}, $posts);
// Устанавливаем заголовки
header('Content-Type: application/rss+xml; charset=' . get_option('blog_charset'), true);
// Начинаем вывод XML
echo '<?xml version="1.0" encoding="' . get_option('blog_charset') . '"?>';
?>
<rss version="2.0"
xmlns:yandex="http://news.yandex.ru"
xmlns:media="http://search.yahoo.com/mrss/"
xmlns:turbo="http://turbo.yandex.ru">
<channel>
<title><?php echo esc_xml(get_bloginfo('name')); ?></title>
<link><?php echo esc_url(home_url()); ?></link>
<description><?php echo esc_xml(get_bloginfo('description')); ?></description>
<language><?php echo esc_xml(get_bloginfo('language')); ?></language>
<lastBuildDate><?php echo esc_xml(gmdate(DATE_RSS)); ?></lastBuildDate>
<pubDate><?php echo esc_xml(gmdate(DATE_RSS)); ?></pubDate>
<guid>https://profile.ru</guid>
<generator>Информационное агенство Деловой журнал Профиль</generator>
<image>
<url><?php echo esc_url($this->get_site_icon_url()); ?></url>
<title><?php echo esc_xml(get_bloginfo('name')); ?></title>
<link><?php echo esc_url(home_url()); ?></link>
</image>
<?php if (empty($posts)): ?>
<item>
<title>No posts for period <?php echo esc_xml($start_date); ?> to <?php echo esc_xml($end_date); ?></title>
<link><?php echo esc_url(home_url()); ?></link>
<description>No posts found for this period</description>
<pubDate><?php echo esc_xml(gmdate(DATE_RSS)); ?></pubDate>
<guid isPermaLink="false">no-posts-<?php echo esc_xml($start_date . '-to-' . $end_date); ?></guid>
</item>
<?php else: ?>
<?php foreach ($posts as $post):
$post = new WP_Post($post);
$post_url = get_permalink($post->ID);
$post_date = gmdate(DATE_RSS, strtotime($post->post_date_gmt));
$excerpt = get_the_excerpt($post->ID);
$content = $this->get_clean_post_content($post);
$thumbnail = get_the_post_thumbnail_url($post->ID, 'large');
// Получаем авторов из плагина Co-Authors
$authors = get_coauthors($post->ID);
?>
<item>
<title><?php echo esc_xml(get_the_title($post->ID)); ?></title>
<link><?php echo esc_url($post_url); ?></link>
<description><?php echo esc_xml($excerpt); ?></description>
<pubDate><?php echo esc_xml($post_date); ?></pubDate>
<?php if (!empty($authors)): ?>
<?php
$author_names = array();
foreach ($authors as $author) {
$author_names[] = $author->display_name;
}
?>
<author><?php echo esc_xml(implode(', ', $author_names)); ?></author>
<?php else: ?>
<?php $default_author = get_the_author_meta('display_name', $post->post_author); ?>
<author><?php echo esc_xml($default_author); ?></author>
<?php endif; ?>
<guid><?php echo esc_url($post_url); ?></guid>
<?php if ($thumbnail): ?>
<imageAnnounce>
<url><?php echo esc_url($thumbnail); ?></url>
<title><?php echo esc_xml(get_the_title($post->ID)); ?></title>
<description><?php echo esc_xml($excerpt); ?></description>
</imageAnnounce>
<?php endif; ?>
<?php if ($thumbnail): ?>
<image>
<url><?php echo esc_url($thumbnail); ?></url>
<title><?php echo esc_xml(get_the_title($post->ID)); ?></title>
<description><?php echo esc_xml($excerpt); ?></description>
<link><?php echo esc_url($post_url); ?></link>
</image>
<?php endif; ?>
<?php
$categories = get_the_category($post->ID);
foreach ($categories as $category) {
echo '<category>' . esc_xml($category->name) . '</category>';
}
?>
<content:encoded>
<![CDATA[<?php echo $content; ?>]]>
</content:encoded>
</item>
<?php endforeach; ?>
<?php endif; ?>
</channel>
</rss>
<?php
}
/**
* Получаем контент поста
*/
private function get_post_content($post) {
$content = apply_filters('the_content', $post->post_content);
$content = str_replace(']]>', ']]&gt;', $content);
return $content;
}
/**
* Активация - сбрасываем rewrite rules
*/
public static function activate() {
flush_rewrite_rules();
}
/**
* Деактивация - чистим rewrite rules
*/
public static function deactivate() {
flush_rewrite_rules();
}
}
// Инициализация
new CustomProfileArticleRSS();
// Хуки активации/деактивации
register_activation_hook(__FILE__, ['CustomProfileArticleRSS', 'activate']);
register_deactivation_hook(__FILE__, ['CustomProfileArticleRSS', 'deactivate']);