mirror of
https://github.com/RSS-Bridge/rss-bridge.git
synced 2024-12-24 21:28:21 +03:00
c8d5c85c76
Allows getting the expected MIME type of the format's output. A corresponding MIME_TYPE constant is also defined in FormatAbstract for the format implementations to overwrite.
164 lines
4.7 KiB
PHP
164 lines
4.7 KiB
PHP
<?php
|
|
/**
|
|
* MrssFormat - RSS 2.0 + Media RSS
|
|
* http://www.rssboard.org/rss-specification
|
|
* http://www.rssboard.org/media-rss
|
|
*
|
|
* Validators:
|
|
* https://validator.w3.org/feed/
|
|
* http://www.rssboard.org/rss-validator/
|
|
*
|
|
* Notes about the implementation:
|
|
*
|
|
* - The item author is not supported as it needs to be an e-mail address to be
|
|
* valid.
|
|
* - The RSS specification does not explicitly allow to have more than one
|
|
* enclosure as every item is meant to provide one "story", thus having
|
|
* multiple enclosures per item may lead to unexpected behavior.
|
|
* On top of that, it requires to have a length specified, which RSS-Bridge
|
|
* can't provide.
|
|
* - The Media RSS extension comes in handy, since it allows to have multiple
|
|
* enclosures, even though they recommend to have only one enclosure because
|
|
* of the one-story-per-item reason. It only requires to specify the URL,
|
|
* everything else is optional.
|
|
* - Since the Media RSS extension has its own namespace, the output is a valid
|
|
* RSS 2.0 feed that works with feed readers that don't support the extension.
|
|
*/
|
|
class MrssFormat extends FormatAbstract {
|
|
const MIME_TYPE = 'application/rss+xml';
|
|
|
|
const ALLOWED_IMAGE_EXT = array(
|
|
'.gif', '.jpg', '.png'
|
|
);
|
|
|
|
public function stringify(){
|
|
$urlPrefix = (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on') ? 'https://' : 'http://';
|
|
$urlHost = (isset($_SERVER['HTTP_HOST'])) ? $_SERVER['HTTP_HOST'] : '';
|
|
$urlPath = (isset($_SERVER['PATH_INFO'])) ? $_SERVER['PATH_INFO'] : '';
|
|
$urlRequest = (isset($_SERVER['REQUEST_URI'])) ? $_SERVER['REQUEST_URI'] : '';
|
|
|
|
$feedUrl = $this->xml_encode($urlPrefix . $urlHost . $urlRequest);
|
|
|
|
$extraInfos = $this->getExtraInfos();
|
|
$title = $this->xml_encode($extraInfos['name']);
|
|
$icon = $extraInfos['icon'];
|
|
|
|
if(!empty($extraInfos['uri'])) {
|
|
$uri = $this->xml_encode($extraInfos['uri']);
|
|
} else {
|
|
$uri = REPOSITORY;
|
|
}
|
|
|
|
$items = '';
|
|
foreach($this->getItems() as $item) {
|
|
$itemTimestamp = $item->getTimestamp();
|
|
$itemTitle = $this->xml_encode($item->getTitle());
|
|
$itemUri = $this->xml_encode($item->getURI());
|
|
$itemContent = $this->xml_encode($this->sanitizeHtml($item->getContent()));
|
|
$entryID = $item->getUid();
|
|
$isPermaLink = 'false';
|
|
|
|
if (empty($entryID) && !empty($itemUri)) { // Fallback to provided URI
|
|
$entryID = $itemUri;
|
|
$isPermaLink = 'true';
|
|
}
|
|
|
|
if (empty($entryID)) // Fallback to title and content
|
|
$entryID = hash('sha1', $itemTitle . $itemContent);
|
|
|
|
$entryTitle = '';
|
|
if (!empty($itemTitle))
|
|
$entryTitle = '<title>' . $itemTitle . '</title>';
|
|
|
|
$entryLink = '';
|
|
if (!empty($itemUri))
|
|
$entryLink = '<link>' . $itemUri . '</link>';
|
|
|
|
$entryPublished = '';
|
|
if (!empty($itemTimestamp)) {
|
|
$entryPublished = '<pubDate>'
|
|
. $this->xml_encode(gmdate(DATE_RFC2822, $itemTimestamp))
|
|
. '</pubDate>';
|
|
}
|
|
|
|
$entryDescription = '';
|
|
if (!empty($itemContent))
|
|
$entryDescription = '<description>' . $itemContent . '</description>';
|
|
|
|
$entryEnclosures = '';
|
|
foreach($item->getEnclosures() as $enclosure) {
|
|
$entryEnclosures .= '<media:content url="'
|
|
. $this->xml_encode($enclosure)
|
|
. '" type="' . getMimeType($enclosure) . '"/>'
|
|
. PHP_EOL;
|
|
}
|
|
|
|
$entryCategories = '';
|
|
foreach($item->getCategories() as $category) {
|
|
$entryCategories .= '<category>'
|
|
. $category . '</category>'
|
|
. PHP_EOL;
|
|
}
|
|
|
|
$items .= <<<EOD
|
|
|
|
<item>
|
|
{$entryTitle}
|
|
{$entryLink}
|
|
<guid isPermaLink="{$isPermaLink}">{$entryID}</guid>
|
|
{$entryPublished}
|
|
{$entryDescription}
|
|
{$entryEnclosures}
|
|
{$entryCategories}
|
|
</item>
|
|
|
|
EOD;
|
|
}
|
|
|
|
$charset = $this->getCharset();
|
|
|
|
$feedImage = '';
|
|
if (!empty($icon) && in_array(substr($icon, -4), self::ALLOWED_IMAGE_EXT)) {
|
|
$feedImage .= <<<EOD
|
|
<image>
|
|
<url>{$icon}</url>
|
|
<title>{$title}</title>
|
|
<link>{$uri}</link>
|
|
</image>
|
|
EOD;
|
|
}
|
|
|
|
/* Data are prepared, now let's begin the "MAGIE !!!" */
|
|
$toReturn = <<<EOD
|
|
<?xml version="1.0" encoding="{$charset}"?>
|
|
<rss version="2.0" xmlns:media="http://search.yahoo.com/mrss/" xmlns:atom="http://www.w3.org/2005/Atom">
|
|
<channel>
|
|
<title>{$title}</title>
|
|
<link>{$uri}</link>
|
|
<description>{$title}</description>
|
|
{$feedImage}
|
|
<atom:link rel="alternate" type="text/html" href="{$uri}"/>
|
|
<atom:link rel="self" href="{$feedUrl}" type="application/atom+xml"/>
|
|
{$items}
|
|
</channel>
|
|
</rss>
|
|
EOD;
|
|
|
|
// Remove invalid non-UTF8 characters
|
|
ini_set('mbstring.substitute_character', 'none');
|
|
$toReturn = mb_convert_encoding($toReturn, $this->getCharset(), 'UTF-8');
|
|
return $toReturn;
|
|
}
|
|
|
|
public function display(){
|
|
$this
|
|
->setContentType(self::MIME_TYPE . '; charset=' . $this->getCharset())
|
|
->callContentType();
|
|
|
|
return parent::display();
|
|
}
|
|
|
|
private function xml_encode($text){
|
|
return htmlspecialchars($text, ENT_XML1);
|
|
}
|
|
}
|