Вывод RSS сайта через pdoResources

Честно говоря, я не очень люблю всякие рассылки и подписки, мне больше нравится использовать RSS.

RSS — семейство XML-форматов, предназначенных для описания лент новостей, анонсов статей, изменений в блогах и т. п. Информация из различных источников, представленная в формате RSS, может быть собрана, обработана и представлена пользователю в удобном для него виде специальными программами-агрегаторами или онлайн-сервисами.

Им очень удобно пользоваться — нужно просто зайти скопировать ссылку и вставить её в свой агрегатор новостей, например www.inoreader.com.
Для пущего удобства можно еще установить расширение для Chrome, чтобы вас регулярно уведомляли о свежих записях.

А теперь давайте посмотрим, как можно генерировать RSS с помощью pdoResources.

Вывод всех заметок в ленту


Давайте вначале сделаем ленту новостей со всеми тикетами сайта.

Для этого нам нужно создать новый ресурс с типом RSS, пустым шаблоном и желательно еще заморозить ему uri:


Я пишу пример для вывода заметок из Tickets, поэтому в условиях вызова pdoResources у меня вот это:
<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
	<channel>
		<title>[[*pagetitle]] / [[++site_name]]</title>
		<link>[[~[[*id]]?scheme=`full`]]</link>
		<description>[[*description:cdata]]</description>
		<language>en</language>
		<copyright></copyright>
		<ttl>120</ttl>
		<atom:link href="[[~[[*id]]?scheme=`full`]]" rel="self" type="application/rss+xml" />
		[[!pdoResources?
			&tpl=`tpl.Tickets.rss.row`
			&parents=`15`
			&depth=`1`
			&limit=`10`
			&showHidden=`1`
			&sortby=`createdon`
			&where=`{"class_key":"Ticket"}`
			&useWeblinkUrl=`1`
			&scheme=`full`
		]]
	</channel>
</rss>
Для нормальной работы ленты необходимо, чтобы все ссылки на сайты и изображения были абсолютными — иначе RSS ридер не сможет их нормально отобразить.

Поэтому пишем и указываем чанк tpl.Tickets.rss.row, в котором introtext будет проходить через фильтры вывода:
<item>
	<title>[[+pagetitle]]</title>
	<link>[[+link]]</link>
	<description>[[+introtext:absURL:cdata]]</description>
	<pubDate>[[+publishedon:date=`%a, %d %b %Y %H:%M:%S %z`]]</pubDate>
	<guid>[[+link]]</guid>
</item>

Как видите, здесь мы оборачиваем introtext в cdata, чтобы там нормально воспринимались теги, и прогоняем его через сниппет absUrl:
<?php
if (empty($input)) {return false;}
$url = $modx->getOption('site_url');

preg_match_all('/(?:href|src)=[\'|"](.*?)[\'|"]/s', $input, $matches);
if (!empty($matches[1])) {
	foreach ($matches[1] as $v) {
		if (strpos($v, '://') === false) {
			$input = str_replace($v, $url . ltrim($v, '/'), $input);
		}
	}
}
return $input;
Этот сниппет добавляет адрес сайта ко всем относительным ссылкам и картинкам, чтобы они нормально отображались в RSS ридере.

Теперь можно открывать вашсайт.ru/rss и проверять его на соответствие стандартам на сервисе validator.w3.org/feed/.


Осталось только добавить специальный тег в head страницы, чтобы уведомлять браузеры и расширения о том, что у вас есть RSS:
<link href="[[++site_url]]rss" rel="alternate" type="application/rss+xml" title="RSS" />
Вместо id 10 укажите свой id с RSS страницей.

Вывод ленты по разделам


Предыдущий вариант RSS подойдёт для небольших сайтов и блогов, где заметок немного и их не нужно разделять по разделам.
Если же у вас сайт покрупнее, то лучше сделать возможность выбора разделов для генерации RSS.

Делается это просто: нужно заменить вызов pdoResources на свой сниппет, например rssFilter, с таким содержимым:
<?php
// Инициализируем пустые переменные
$parents = $title = $alias = array();
// Слушаем переменную blogs
if (!empty($_GET['blogs'])) {
	// Разбиваем список блоков
	$blogs = array_map('trim', explode(',', $_GET['blogs']));
	// Выбираем подходящие по alias секции
	if (!empty($blogs)) {
		$q = $modx->newQuery('TicketsSection', array('class_key' => 'TicketsSection', 'published' => 1, 'deleted' => 0));
		$q->where(array('alias:IN' => $blogs));
		$q->sortby('menuindex','ASC');
		$q->select('id,pagetitle,alias');
		if ($q->prepare() && $q->stmt->execute()) {
			// Распихиваем результаты по переменным
			while ($row = $q->stmt->fetch(PDO::FETCH_ASSOC)) {
				$parents[] = $row['id'];
				$title[] = $row['pagetitle'];
				$alias[] = $row['alias'];
			}
		}
	}
}
// Если были указаны конкретные секции - добавляем их в параметры вызова pdoResources
if (!empty($parents)) {
	$scriptProperties['parents'] = implode(',', $parents);
	$scriptProperties['depth'] = 0;
}
// Формируем более понятный заголовок
if (!empty($title)) {
	$modx->setPlaceholder('title', implode(', ', $title));
}
else {
	$modx->setPlaceholder('title', 'Все');
}
// И ссылку на эту ленту новостей
if (!empty($alias)) {
	$modx->setPlaceholder('params', '?blogs='.implode(',',$alias));
}
// Вызываем pdoResources
return $modx->runSnippet('pdoResources', $scriptProperties);

То есть, этот сниппет слушает $_GET['blogs'] и если там есть что-то подходящее, то фильтрует вывод тикетов по секциям и выставляет плейсхолдеры [[+title]] и [[+params]] на страницу с RSS.

Теперь меняем нашу страницу с выводом RSS (показываю только измененные куски, чтобы было понятнее):

Заголовок страницы:
<title>[[!+title]] / [[++site_name]]</title>
Ссылки на ленту:
<link>[[~[[*id]]?scheme=`full`]][[+params]]</link>
<atom:link href="[[~[[*id]]?scheme=`full`]][[+params]]" rel="self" type="application/rss+xml" />
Вызов сниппета:
[[!rssFilter?
Вот, теперь сравните вывод всех заметок и только новостей.

Теперь можно еще сделать симпатичную страницу для генерации специальной ссылки только на определенные разделы, но это уже на ваше усмотрение.

Еще можно изменить вывод мета-тега RSS так, чтобы когда пользователь находится на странице раздела тикетов, он получал ссылку именно на этот раздел, а не на всё подряд:
<link href="[[++site_url]]rss[[*class_key:is=`TicketsSection`:then=`?blogs=[[*alias]]`]]" rel="alternate" type="application/rss+xml" title="RSS" />

Вот и всё, спасибо за внимание!
Василий Наумкин
24 апреля 2014, 07:18
modx.pro
23
7 403
+3

Комментарии: 11

TITAN-UZ
25 августа 2014, 15:44
0
Как вывести дату по английски?
    TITAN-UZ
    25 августа 2014, 16:13
    0
    Решил проблему со сниппетом
    snippet name: dateLocale

    <?php
    setlocale(LC_ALL, 'en_US');
     
    return strftime($options,$input);
    Вывод
    [[+publishedon:dateLocale=`%a, %d %b %Y %H:%M:%S %z`]]
    Сергей Шлоков
    27 декабря 2015, 14:21
    0
    Делаю настройку по инструкции для простого блога (1 часть заметки). Всё сделал на написанному, только id свой указал. В итоге по адресу site.ru/rss получаю циклическую перезагрузку. Вообще не хочет отображать страницу с типом RSS. С чем это может быть связано? Подскажите кто знает.
      Алексей Федоров
      27 декабря 2015, 19:03
      +1
      У меня страница выходит вот такой — modzone.ru/rss/ то есть имеем в конце слеш. Возможно стоит [[Canonical]] в хеде (либо настройка в .htaccess), и устраивает войну с замороженным uri. Но это так из разряда предположений. Других вариантов я не вижу.
        Сергей Шлоков
        27 декабря 2015, 19:17
        0
        Точно. Виноват слэш. Спасибо.
        П.С. Каноникал не делает редирект, он всего лишь прописывает url страницы. Но у меня его нет. :) Возможно в nginx правило такое настроено. Ещё раз спасибо.
          Алексей Федоров
          27 декабря 2015, 20:50
          0
          Каноникал не делает редирект, он всего лишь прописывает url страницы.
          Буду знать)
      Леонид
      05 декабря 2018, 22:19
      0
      Приветствую, вопрос про RSS, но немного отойдя от вопроса выше: Access-Control-Allow-Origin: *, как его запихнуть на RSS feed, если в структуре вывода фида участвует шаблонизатор пустого шаблона и если включаешь заголовок через шаблон, то фид просто не проходит валидацию?
        Леонид
        16 декабря 2018, 23:51
        0
        Отвечаю на свой же вопрос по CORS, создаём сниппет с содержимым
        <?php
        header('Content-Type: application/rss+xml; charset=UTF-8');
        header('Access-Control-Allow-Origin: *');
        Перед выводом страницы вставляем то, чем обозвали
        [[!RSS-CORS]]<?xml version="1.0" encoding="UTF-8"?>
        <rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
         <channel>
         <title>[[*pagetitle]] / [[++site_name]]</title>
         <link>[[~[[*id]]?scheme=`full`]]</link>
         <description>[[*description:cdata]]</description>
         <language>ru</language>
         <copyright></copyright>
         <ttl>120</ttl>
         <atom:link href="[[~[[*id]]?scheme=`full`]]" rel="self" type="application/rss+xml" />
         [[!pdoResources?
         &tpl=`tpl-rss`
         &parents=`17`
         &depth=`2`
         &showHidden=`1`
         &sortby=`createdon`
         &includeContent=`1`
         ]]
         </channel>
        </rss>
        Дмитрий
        12 декабря 2018, 02:07
        0
        Приветствую.
        MODX Revolution 2.7.0-pl

        Что-то ни первый вариант, ни второй у меня не сработал. Делал все по инструкции.

        1. Создал ресурс с типом содержимого rss, пустым шаблоном и заморозил ему uri — rss, а также включил «Использовать текущий псевдоним в пути псевдонимов».

        2. В поле «Содержимое» вставил код:
        <?xml version="1.0" encoding="UTF-8"?>
        <rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
        	<channel>
        		<title>[[*pagetitle]] / [[++site_name]]</title>
        		<link>[[~[[*id]]?scheme=`full`]]</link>
        		<description>[[*description:cdata]]</description>
        		<language>en</language>
        		<copyright></copyright>
        		<ttl>120</ttl>
        		<atom:link href="[[~[[*id]]?scheme=`full`]]" rel="self" type="application/rss+xml" />
        		[[!pdoResources?
        			&tpl=`tpl.Tickets.rss.row`
        			&parents=`7`
        			&depth=`1`
        			&limit=`10`
        			&showHidden=`1`
        			&sortby=`createdon`
        			&where=`{"class_key":"Ticket"}`
        			&useWeblinkUrl=`1`
        			&scheme=`full`
        		]]
        	</channel>
        </rss>
        Вместо &parents=`15` прописывал свой id 7, пробовал 0 и др. id ресурсов-контейнеров.

        3. Создал чанк tpl.Tickets.rss.row с содержимым:
        <item>
        	<title>[[+pagetitle]]</title>
        	<link>[[+link]]</link>
        	<description>[[+introtext:absURL:cdata]]</description>
        	<pubDate>[[+publishedon:date=`%a, %d %b %Y %H:%M:%S %z`]]</pubDate>
        	<guid>[[+link]]</guid>
        </item>

        4. Создал сниппет absUrl с содержимым:
        <?php
        if (empty($input)) {return false;}
        $url = $modx->getOption('site_url');
        
        preg_match_all('/(?:href|src)=[\'|"](.*?)[\'|"]/s', $input, $matches);
        if (!empty($matches[1])) {
        	foreach ($matches[1] as $v) {
        		if (strpos($v, '://') === false) {
        			$input = str_replace($v, $url . ltrim($v, '/'), $input);
        		}
        	}
        }
        return $input;

        5. открываю ресурс mysite/rss и мне из динамического содержимого выводит только лишь title, link, description и atom:link.

        Вот исходный код страницы, но это уже дает второй вариант (данной инструкции), который исходным кодом страницы незначительно отличается от первого:
        <?xml version="1.0" encoding="UTF-8"?>
        <rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
        	<channel>
        		<title>Все / Название сайта</title>
        		<link>https://sitename.ru/rss</link>
        		<description><![CDATA[Мой rss канал]]></description>
        		<language>en</language>
        		<copyright></copyright>
        		<ttl>120</ttl>
        		<atom:link href="https://sitename.ru/rss" rel="self" type="application/rss+xml" />
        		
        	</channel>
        </rss>

        Не пойму, почему не выводит остальное? В чем может быть причина?

        P/S: В конце первого варианта написано:
        Вместо id 10 укажите свой id с RSS страницей.
        Однако в примере id 10 нигде нет. Может я что-то упустил?
          Дмитрий
          12 декабря 2018, 02:19
          0
          Да, pdoTools установлен, Tickets — установил перед использованием инструкции.
          Вроде должно выводить, а не выводит.
          Aleksanders
          11 февраля 2021, 15:51
          0
          Ребята, 3й день бьюсь и не могу разобраться, почему у меня в rss дата стоит на всех новостях:
          <pubDate>Thu, 01 Jan 1970 03:33:40 +0300</pubDate>
          Создал сниппет:
          <?php
          setlocale(LC_ALL, 'en_US');
          return strftime($options,$input);
          В tpl прописал:
          [[+publishedon:dateLocale=`%a, %d %b %Y %H:%M:%S %z`]]
          Пробовал сниппет такой:
          <?php
          setlocale(LC_ALL, 'en_US.UTF-8');
          if ( $val == '' ) $val=time();
          if ($format == '' ) $format = "%a, %d %b %Y %H:%M:%S";
          return strftime($format, $val);
          Всё равно на всех новостях дата 1970 год. Подскажите, как исправить?
            Авторизуйтесь или зарегистрируйтесь, чтобы оставлять комментарии.
            11