last update

This commit is contained in:
gerchek 2022-10-10 16:15:19 +05:00
parent d494df71d6
commit 35db57fd37
28 changed files with 2103 additions and 1 deletions

View File

@ -0,0 +1,164 @@
<?php
/**
* Author : Denis-Florin Rendler
* Date : 21/09/15
* Copyright (c) 2015 Denis-Florin Rendler <connect@rendler.me>
*/
namespace KoderHut\RssFeedster;
use Illuminate\Support\Facades\App;
use Route,
Url;
use Cms\Classes\Controller;
use System\Classes\PluginBase,
System\Classes\PluginManager;
use KoderHut\RssFeedster\Classes\Feedster,
KoderHut\RssFeedster\Classes\DataSource\NewsSource,
KoderHut\RssFeedster\Classes\View\XmlRenderer,
KoderHut\RssFeedster\Models\Settings,
KoderHut\RssFeedster\Classes\Support\Adapters\NewsXmlAdapter,
KoderHut\RssFeedster\Classes\Contracts\IAdapter;
/**
* RssFeed Plugin Information File
*/
class Plugin
extends PluginBase
{
/**
* Namespace const
*/
const KODERHUT_RSSFEEDSTER_NS = 'KoderHut\RssFeedster';
/**
* Define the plug-in dependencies
*
* @var array Plugin dependencies
*/
public $require = ['Indikator.News'];
/**
* Returns information about this plugin.
*
* @return array
*/
public function pluginDetails()
{
return [
'name' => 'koderhut.rssfeedster::lang.plugin.name',
'description' => 'koderhut.rssfeedster::lang.plugin.description',
'author' => 'Denis-Florin Rendler (KoderHut)',
'icon' => 'icon-rss',
'homepage' => 'https://github.com/rendler-denis/rssfeedster',
];
}
/**
* Set up the route for the RSS builder
*/
public function boot()
{
$rssUrl = Settings::get('feed_url');
Route::get('/{locale?}'.$rssUrl, 'KoderHut\RssFeedster\Controllers\Rss@buildRssFeed');
$this->app->bind('KoderHut\RssFeedster\Feed', function($app)
{
return new Feedster();
});
}
/**
* Register our data source and renderer
*/
public function register()
{
/**
* Set up the data source for the feed
*/
$this->app->bind('KoderHut\RssFeedster\DataSource', function($app)
{
$config['page'] = Settings::get('post_page');
$config['comments_anchor'] = Settings::get('comments_anchor');
$config['controller'] = new Controller();
return new NewsSource($config);
});
/**
* Set up the adapter interface between the XML renderer and
* the blog posts data
*/
$this->app->bind(IAdapter::DI_NAMESPACE, function () {
return new NewsXmlAdapter();
});
/**
* Set up the feed renderer
*/
$this->app->bind('KoderHut\RssFeedster\Renderer', function($app)
{
$config = [
'description' => Settings::get('feed_description'),
'title' => Settings::get('feed_title'),
'category' => Settings::get('feed_category'),
'copyright' => Settings::get('feed_copyright'),
'language' => App::getLocale(),
'link' => Url::action('KoderHut\RssFeedster\Controllers\Rss@buildRssFeed'),
];
$pluginPath = PluginManager::instance()->getPluginPath(Plugin::KODERHUT_RSSFEEDSTER_NS);
$xmlTemplate = $pluginPath . DIRECTORY_SEPARATOR . 'resources'
. DIRECTORY_SEPARATOR . 'feed-template.xml';
return
new XmlRenderer(
$config,
file_get_contents($xmlTemplate),
$this->app->make(IAdapter::DI_NAMESPACE)
);
});
return;
}
/**
* Register the plug-in settings
*
* @return array
*/
public function registerSettings()
{
return [
'settings' => [
'label' => 'koderhut.rssfeedster::lang.plugin.name',
'description' => 'koderhut.rssfeedster::lang.settings.base.description',
'icon' => 'icon-rss',
'class' => 'KoderHut\RssFeedster\Models\Settings',
'order' => 500,
'keywords' => 'rss feed',
'category' => 'KoderHut',
'permissions' => ['koderhut.rssfeedster.access_config'],
]
];
}
/**
* Register plug-in permissions
*
* @return array
*/
public function registerPermissions()
{
return [
'koderhut.rssfeedster.access_config' => [
'label' => 'koderhut.rssfeedster::lang.messages.permissions.access_config_label',
'tab' => 'koderhut.rssfeedster::lang.plugin.name'
],
];
}
}

View File

@ -0,0 +1,22 @@
## RssFeedster
> Note: This plug-in extends and depends on the [RainLab's Blog](https://octobercms.com/plugin/rainlab-blog) plug-in to generate the feed.
An on-the-fly, W3C validated, customisable RSS feed builder plug-in.
The generated RSS feed was built according to the W3C standards. It includes all the required tags as well as a few optional ones, like copyright, feed category, feed language. These optional tags can help feed aggregators to better promote your feed.
The feed is built on request, so it always generates an up-to-date feed.
The plug-in is very customisable by both end-user as well as extendable by other developers.
## Features
- customisable URL
- feed built on request
- includes optional RSS fields for better integration with feed aggregators
- data source and renderer easily extendable by developers.
## License
The License is MIT with the exception that you are NOT allowed to sell the plug-in under ANY circumstance.
You can install, debug and fix the code for your clients and charge for those services but you are NOT allowed to charge for the plug-in itself.
If you are in doubt about the license you can contact me at connect [ at ] rendler.me

View File

@ -0,0 +1,150 @@
<?php
/**
* Author : Denis-Florin Rendler
* Date : 21/09/15
* Copyright (c) 2015 Denis-Florin Rendler <connect@rendler.me>
*/
namespace KoderHut\RssFeedster\Classes;
use October\Rain\Exception\ApplicationException;
use KoderHut\RssFeedster\Classes\Contracts\IDataSource,
KoderHut\RssFeedster\Classes\Contracts\IRenderer,
KoderHut\RssFeedster\Models\Settings,
KoderHut\RssFeedster\Classes\Support\Facades\Renderer,
KoderHut\RssFeedster\Classes\Support\Facades\DataSource;
/**
* Class Feedster
* Service class in charge of collecting the feed data
* and rendering it
*
* @package KoderHut\RssFeedster\Classes
*/
class Feedster
{
/**
* The feed generator name
*/
const FEED_GENERATOR_NAME = 'KoderHut.eu - RSSFeedster for OctoberCMS';
/**
* Feed title
*
* @var string
*/
protected $feedTitle = '';
/**
* Maximum number of rows to retrieve and render
*
* @var int
*/
protected $dataRowsLimit = 1;
/**
* Either to display the full content
*
* @var bool
*/
protected $fullContent = false;
/**
* Data source for retrieving the feed data
*
* @var IDataSource
*/
protected $source = null;
/**
* Feed renderer
*
* @var IRenderer
*/
protected $renderer = null;
/**
* Constructor
*
* @throws ApplicationException
*/
public function __construct(IDataSource $source = null, IRenderer $renderer = null, Settings $config = null)
{
$config = $config instanceof Settings ? $config : Settings::instance();
$this->validateSettings($config);
$this->feedTitle = $config->feed_title;
$this->dataRowsLimit = $config->post_max_number;
$this->fullContent = $config->post_full_content;
$this->source = $source ?: DataSource::getFacadeRoot();
$this->renderer = $renderer ?: Renderer::getFacadeRoot();
}
/**
* Validate required settings
*
* @param Settings $settings
*
* @return bool
*
* @throws ApplicationException
*/
protected function validateSettings(Settings $settings)
{
if (empty($settings->feed_title)) {
throw new ApplicationException('Feed Title is required!');
}
if ($settings->post_max_number <= 0) {
$this->dataRowsLimit = 1000;
}
return true;
}
/**
* Set a data source for building the feed
*
* @param IDataSource $source
*/
public function setSource(IDataSource $source)
{
$this->source = $source;
}
/**
* Set a renderer for the feed
*
* @param Renderer $renderer
*/
public function setRenderer(IRenderer $renderer)
{
$this->renderer = $renderer;
}
/**
* Return the rendered feed data
*
* @return mixed
*/
public function getFeed()
{
$feedData = $this->source->getData($this->dataRowsLimit);
$renderedData = $this->renderer->renderData($feedData);
return $renderedData;
}
/**
* Return the content-type for this renderer
*
* @return string
*/
public function getContentType()
{
return $this->renderer->getContentType();
}
}

View File

@ -0,0 +1,31 @@
<?php
/**
* Author : Denis-Florin Rendler
* Date : 13/10/15
* Copyright (c) 2015 Denis-Florin Rendler <connect@rendler.me>
*/
namespace KoderHut\RssFeedster\Classes\Contracts;
/**
* Interface IAdapter
*
* @package KoderHut\RssFeedster\Classes\Contracts
*/
interface IAdapter
{
/**
* Namespace const used for DI
*/
const DI_NAMESPACE = 'KoderHut\RssFeedster\Adapter';
/**
* Method used to connect a data source to a renderer
*
* @param mixed $item
* @param mixed $dataItem
*
* @return mixed
*/
public function getDataValue($item, $dataItem);
}

View File

@ -0,0 +1,32 @@
<?php
/**
* Author : Denis-Florin Rendler
* Date : 21/09/15
* Copyright (c) 2015 Denis-Florin Rendler <connect@rendler.me>
*/
namespace KoderHut\RssFeedster\Classes\Contracts;
/**
* Interface IDataSource
*
* Data source interface
*
* @package KoderHut\RssFeedster\Classes\Contracts
*/
interface IDataSource
{
/**
* Load the data from source
*
* @return mixed
*/
public function loadData();
/**
* Load, if not laready loaded, and return the data
*
* @return mixed
*/
public function getData();
}

View File

@ -0,0 +1,34 @@
<?php
/**
* Author : Denis-Florin Rendler
* Date : 21/09/15
* Copyright (c) 2015 Denis-Florin Rendler <connect@rendler.me>
*/
namespace KoderHut\RssFeedster\Classes\Contracts;
/**
* Interface IRenderer
*
* Renderer interface
*
* @package KoderHut\RssFeedster\Classes\Interfaces
*/
interface IRenderer
{
/**
* Render the data
*
* @param $data
*
* @return mixed
*/
public function renderData($data);
/**
* Return the content-type for this renderer
*
* @return string
*/
public function getContentType();
}

View File

@ -0,0 +1,106 @@
<?php
namespace KoderHut\RssFeedster\Classes\DataSource;
use \Indikator\News\Models\Posts;
use KoderHut\RssFeedster\Classes\Contracts\IDataSource;
class NewsSource implements IDataSource
{
/**
* Maximum number of items to display in the feed
*/
const MAX_NO_ITEMS = 10;
/**
* Cache for the data
*
* @var array|mixed
*/
protected $data = [];
/**
* Disaply the full content of the post
*
* @var bool
*/
protected $displayFullContent = false;
/**
* Constructor
*
* @param array $params
*/
public function __construct($params = null)
{
if (null === $params) {
return $this;
}
$this->page = $params['page'];
$this->controller = $params['controller'];
$this->commentsAnchor = $params['comments_anchor'];
}
/**
* Load the news
*
* @param int $maxItems
*
* @return mixed
*/
public function loadData($maxItems = self::MAX_NO_ITEMS)
{
$posts = null;
if (!empty($this->data)) {
return $this->data;
}
$model = new Posts();
$locale = request('locale');
$posts = $model->listFrontEnd([
'sort' => 'published_at desc',
'perPage' => $maxItems,
'locale' => in_array($locale,['ru','en','tk'])? $locale : 'en'
]);
foreach ($posts as $post) {
$post->setUrl($this->page, $this->controller);
// $post->comments_url = "{$post->url}/#{$this->commentsAnchor}";
$post->feed_content = true === $this->displayFullContent ? $post->content : $post->introductory;
}
return $this->data = $posts;
}
/**
* Retrieve posts from cache or load them and then return them
*
* @param int $maxItems
*
* @return mixed
*/
public function getData($maxItems = self::MAX_NO_ITEMS)
{
if (null !== $this->data && !empty($this->data)) {
return $this->data;
}
$this->data = $this->loadData($maxItems);
return $this->data;
}
/**
* Set if we display the full post content or a summary
*
* @param bool $fullContent
*/
public function setDisplayFullContent($fullContent = true)
{
$this->displayFullContent = $fullContent;
}
}

View File

@ -0,0 +1,117 @@
<?php
/**
* Author : Denis-Florin Rendler
* Date : 21/09/15
* Copyright (c) 2015 Denis-Florin Rendler <connect@rendler.me>
*/
namespace KoderHut\RssFeedster\Classes\DataSource;
use KoderHut\RssFeedster\Classes\Contracts\IDataSource;
use RainLab\Blog\Models\Post;
/**
* Class PostsSource
*
* Feed data source retrieving blog posts.
* Requires: RainLab.Blog plug-in
*
* @package KoderHut\RssFeedster\Classes\DataSource
*/
class PostsSource
implements IDataSource
{
/**
* Maximum number of items to display in the feed
*/
const MAX_NO_ITEMS = 10;
/**
* Cache for the data
*
* @var array|mixed
*/
protected $data = [];
/**
* Disaply the full content of the post
*
* @var bool
*/
protected $displayFullContent = false;
/**
* Constructor
*
* @param array $params
*/
public function __construct($params = null)
{
if (null === $params) {
return $this;
}
$this->page = $params['page'];
$this->controller = $params['controller'];
$this->commentsAnchor = $params['comments_anchor'];
}
/**
* Load the blog posts
*
* @param int $maxItems
*
* @return mixed
*/
public function loadData($maxItems = self::MAX_NO_ITEMS)
{
$posts = null;
if (!empty($this->data)) {
return $this->data;
}
$model = new Post();
$posts = $model->listFrontEnd([
'sort' => 'published_at desc',
'perPage' => $maxItems,
]);
foreach ($posts as $post) {
$post->setUrl($this->page, $this->controller);
$post->comments_url = "{$post->url}/#{$this->commentsAnchor}";
$post->feed_content = true === $this->displayFullContent ? $post->content : $post->summary;
}
return $this->data = $posts;
}
/**
* Retrieve posts from cache or load them and then return them
*
* @param int $maxItems
*
* @return mixed
*/
public function getData($maxItems = self::MAX_NO_ITEMS)
{
if (null !== $this->data && !empty($this->data)) {
return $this->data;
}
$this->data = $this->loadData($maxItems);
return $this->data;
}
/**
* Set if we display the full post content or a summary
*
* @param bool $fullContent
*/
public function setDisplayFullContent($fullContent = true)
{
$this->displayFullContent = $fullContent;
}
}

View File

@ -0,0 +1,108 @@
<?php
/**
* Author : Denis-Florin Rendler
* Date : 13/10/15
* Copyright (c) 2015 Denis-Florin Rendler <connect@rendler.me>
*/
namespace KoderHut\RssFeedster\Classes\Support\Adapters;
use DomElement,
DOMCdataSection;
use KoderHut\RssFeedster\Classes\Contracts\IAdapter;
/**
* Class BlogPostXmlAdapter
* Adapter between the Posts data source and the XML renderer
*
* @package KoderHut\RssFeedster\Classes\Support\Adapters
*/
class BlogPostXmlAdapter
implements IAdapter
{
/**
* Transform Post data into XML elements
*
* @param mixed $item
* @param mixed $dataItem
*
* @return void
*/
public function getDataValue($item, $dataItem)
{
switch ($item->tagName) {
case 'title':
$item->nodeValue = $dataItem->title;
break;
case 'link':
case 'guid':
$item->nodeValue = $dataItem->url;
break;
case 'pubDate':
$item->nodeValue = $dataItem->published_at->format('r');
break;
case 'dc:creator':
if (!$dataItem->user instanceof User) {
break;
}
$creator = new DOMCdataSection(
"{$dataItem->user->email} ({$dataItem->user->full_name})"
);
$item->appendChild($creator);
break;
case 'category':
$categories = $dataItem->categories;
if (!empty($item->nodeValue)) {
break;
}
if (0 !== $categories->count()) {
$this->addCategories($item, $categories->lists('name'));
}
break;
case 'description':
$description = new DOMCdataSection($dataItem->summary);
$item->appendChild($description);
break;
case 'content:encoded':
$content = new DOMCdataSection($dataItem->feed_content);
$item->appendChild($content);
break;
case 'comments':
$item->nodeValue = $dataItem->comments_url;
break;
default:
break;
}
}
/**
* Parse and add the categories of the post to the current item
*
* @param DomElement $categoryNode
* @param array $categories
*/
protected function addCategories(DomElement $categoryNode, $categories)
{
$itemNode = $categoryNode->parentNode;
$itemNode->removeChild($categoryNode);
foreach ($categories as $categoryName) {
$newCategNode = clone($categoryNode);
$newCategNode->appendChild(new DOMCdataSection($categoryName));
$itemNode->appendChild($newCategNode);
}
}
}

View File

@ -0,0 +1,97 @@
<?php
namespace KoderHut\RssFeedster\Classes\Support\Adapters;
use DomElement,
DOMCdataSection;
use KoderHut\RssFeedster\Classes\Contracts\IAdapter;
class NewsXmlAdapter implements IAdapter
{
/**
* Transform Post data into XML elements
*
* @param mixed $item
* @param mixed $dataItem
*
* @return void
*/
public function getDataValue($item, $dataItem)
{
switch ($item->tagName) {
case 'title':
$item->nodeValue = $dataItem->title;
break;
case 'link':
case 'guid':
$item->nodeValue = $dataItem->url;
break;
case 'pubDate':
$item->nodeValue = $dataItem->published_at->format('r');
break;
case 'dc:creator':
if (!$dataItem->user instanceof User) {
break;
}
$creator = new DOMCdataSection(
"{$dataItem->user->email} ({$dataItem->user->full_name})"
);
$item->appendChild($creator);
break;
// case 'category':
// $categories = $dataItem->categories;
//
// if (!empty($item->nodeValue)) {
// break;
// }
//
// if (0 !== $categories->count()) {
// $this->addCategory($item, $categories->lists('name'));
// }
// break;
case 'description':
$description = new DOMCdataSection($dataItem->seo_desc);
$item->appendChild($description);
break;
case 'content:encoded':
$content = new DOMCdataSection($dataItem->feed_content);
$item->appendChild($content);
break;
case 'comments':
$item->nodeValue = $dataItem->comments_url;
break;
default:
break;
}
}
// /**
// * Parse and add the categories of the post to the current item
// *
// * @param DomElement $categoryNode
// * @param array $categories
// */
// protected function addCategory(DomElement $categoryNode, $categories)
// {
// $itemNode = $categoryNode->parentNode;
// $itemNode->removeChild($categoryNode);
//
// foreach ($categories as $categoryName) {
// $newCategNode = clone($categoryNode);
// $newCategNode->appendChild(new DOMCdataSection($categoryName));
//
// $itemNode->appendChild($newCategNode);
// }
// }
}

View File

@ -0,0 +1,29 @@
<?php
/**
* Author : Denis-Florin Rendler
* Date : 21/09/15
* Copyright (c) 2015 Denis-Florin Rendler <connect@rendler.me>
*/
namespace KoderHut\RssFeedster\Classes\Support\Facades;
use Illuminate\Support\Facades\Facade;
/**
* Class DataSource
* Facade for the data source object
*
* @package KoderHut\RssFeedster\Classes\Support\Facades
*/
class DataSource
extends Facade
{
/**
* Return the facade namespace
*
* @return string
*/
protected static function getFacadeAccessor() {
return 'KoderHut\RssFeedster\DataSource';
}
}

View File

@ -0,0 +1,30 @@
<?php
/**
* Author : Denis-Florin Rendler
* Date : 21/09/15
* Copyright (c) 2015 Denis-Florin Rendler <connect@rendler.me>
*/
namespace KoderHut\RssFeedster\Classes\Support\Facades;
use Illuminate\Support\Facades\Facade;
/**
* Class Renderer
* Facade for the renderer object
*
* @package KoderHut\RssFeedster\Classes\Support\Facades
*/
class Renderer
extends Facade
{
/**
* Return the facade namespace
*
* @return string
*/
protected static function getFacadeAccessor() {
return 'KoderHut\RssFeedster\Renderer';
}
}

View File

@ -0,0 +1,134 @@
<?php
/**
* Author : Denis-Florin Rendler
* Date : 21/09/15
* Copyright (c) 2015 Denis-Florin Rendler <connect@rendler.me>
*/
namespace KoderHut\RssFeedster\Classes\View;
use KoderHut\RssFeedster\Classes\Contracts\IAdapter;
use KoderHut\RssFeedster\Classes\Contracts\IRenderer as Renderer;
use KoderHut\RssFeedster\Classes\Feedster;
/**
* Class XmlRenderer
* Build an RSS Atom valid feed xml file based on the data passed
*
* @package KoderHut\RssFeedster\Classes\View
*/
class XmlRenderer
implements Renderer
{
/**
* Content type header
*/
const RENDERER_CONTENT_TYPE = 'text/xml';
/**
* Sections that need to be wrapped into a CDATA element
*
* @var array
*/
private $cdataSections = ['link', 'description', ];
/**
* Init method used to initialize the feed document
*
* @param array $channelData base channel data taken from settings
* @param string $xmlTemplate
* @param IAdapter $adapter
*/
public function __construct($channelData = [], $xmlTemplate = null, IAdapter $adapter = null)
{
$this->adapter = $adapter;
$xmlDoc = new \DOMDocument('1.0', 'UTF-8');
$xmlDoc->formatOutput = true;
if (null !== $xmlTemplate) {
$xmlDoc->loadXML($xmlTemplate);
}
$this->itemTmpl = $xmlDoc->getElementsByTagName('item')->item(0);
if (null !== $this->itemTmpl) {
$channelElement = $xmlDoc->getElementsByTagName('channel')->item(0);
$channelElement->removeChild($this->itemTmpl);
}
$this->addChannelData($channelData, $xmlDoc);
$this->xml = $xmlDoc;
}
/**
* Build the feed items and attach them to the feed document
*
* @param array|Illuminate\Contracts\Pagination\Paginator $data
*
* @return mixed
*/
public function renderData($data = [])
{
$channel = $this->xml->getElementsByTagName('channel')->item(0);
if (0 == count($data) || null === $this->itemTmpl || !$this->itemTmpl->hasChildNodes()) {
return $this->xml->saveXML();
}
foreach ($data as $dataItem) {
$xmlItem = clone($this->itemTmpl);
foreach ($xmlItem->childNodes as $node) {
if (!$node instanceof \DOMElement) {
continue;
}
$this->adapter->getDataValue($node, $dataItem);
}
$channel->appendChild($xmlItem);
}
return $this->xml->saveXML();
}
/**
* Return the content-type for this renderer
*
* @return string
*/
public function getContentType()
{
return self::RENDERER_CONTENT_TYPE;
}
/**
* Add the channel data
*
* @param \DomDocument $xmlDoc
* @param array $channelData
*/
protected function addChannelData($channelData, $xmlDoc)
{
// $xmlDoc->getElementsByTagName('generator')->item(0)
// ->textContent = Feedster::FEED_GENERATOR_NAME;
$xmlDoc->getElementsByTagName('lastBuildDate')->item(0)
->textContent = date('r');
foreach ($channelData as $element => $elValue) {
$xmlItem = $xmlDoc->getElementsByTagName($element)->item(0);
if ($xmlItem instanceof \DOMElement) {
$xmlItem->textContent = $elValue;
}
}
$atomLink = $xmlDoc->getElementsByTagNameNS($xmlDoc->lookupNamespaceUri('atom'), 'link')->item(0);
if ($atomLink instanceof \DOMElement) {
$atomLink->setAttribute('href', isset($channelData['link']) ? $channelData['link']: '');
}
}
}

View File

@ -0,0 +1,97 @@
"Afrikaans": af
"Albanian": sq
"Basque": eu
"Belarusian": be
"Bulgarian": bg
"Catalan": ca
"Chinese (Simplified)": zh-cn
"Chinese (Traditional)": zh-tw
"Croatian": hr
"Czech": cs
"Danish": da
"Dutch": nl
"Dutch (Belgium)": nl-be
"Dutch (Netherlands)": nl-nl
"English": en
"English (Australia)": en-au
"English (Belize)": en-bz
"English (Canada)": en-ca
"English (Ireland)": en-ie
"English (Jamaica)": en-jm
"English (New Zealand)": en-nz
"English (Phillipines)": en-ph
"English (South Africa)": en-za
"English (Trinidad)": en-tt
"English (United Kingdom)": en-gb
"English (United States)": en-us
"English (Zimbabwe)": en-zw
"Estonian": et
"Faeroese": fo
"Finnish": fi
"French": fr
"French (Belgium)": fr-be
"French (Canada)": fr-ca
"French (France)": fr-fr
"French (Luxembourg)": fr-lu
"French (Monaco)": fr-mc
"French (Switzerland)": fr-ch
"Galician": gl
"Gaelic": gd
"German": de
"German (Austria)": de-at
"German (Germany)": de-de
"German (Liechtenstein)": de-li
"German (Luxembourg)": de-lu
"German (Switzerland)": de-ch
"Greek": el
"Hawaiian": haw
"Hungarian": hu
"Icelandic": is
"Indonesian": in
"Irish": ga
"Italian": it
"Italian (Italy)": it-it
"Italian (Switzerland)": it-ch
"Japanese": ja
"Korean": ko
"Macedonian": mk
"Norwegian": no
"Polish": pl
"Portuguese": pt
"Portuguese (Brazil)": pt-br
"Portuguese (Portugal)": pt-pt
"Romanian": ro
"Romanian (Moldova)": ro-mo
"Romanian (Romania)": ro-ro
"Russian": ru
"Russian (Moldova)": ru-mo
"Russian (Russia)": ru-ru
"Serbian": sr
"Slovak": sk
"Slovenian": sl
"Spanish": es
"Spanish (Argentina)": es-ar
"Spanish (Bolivia)": es-bo
"Spanish (Chile)": es-cl
"Spanish (Colombia)": es-co
"Spanish (Costa Rica)": es-cr
"Spanish (Dominican Republic)": es-do
"Spanish (Ecuador)": es-ec
"Spanish (El Salvador)": es-sv
"Spanish (Guatemala)": es-gt
"Spanish (Honduras)": es-hn
"Spanish (Mexico)": es-mx
"Spanish (Nicaragua)": es-ni
"Spanish (Panama)": es-pa
"Spanish (Paraguay)": es-py
"Spanish (Peru)": es-pe
"Spanish (Puerto Rico)": es-pr
"Spanish (Spain)": es-es
"Spanish (Uruguay)": es-uy
"Spanish (Venezuela)": es-ve
"Swedish": sv
"Swedish (Finland)": sv-fi
"Swedish (Sweden)": sv-se
"Turkish": tr
"Turkmen": tk
"Ukranian": uk

View File

@ -0,0 +1,32 @@
<?php
/**
* Author : Denis-Florin Rendler
* Date : 21/09/15
* Copyright (c) 2015 Denis-Florin Rendler <connect@rendler.me>
*/
namespace KoderHut\RssFeedster\Controllers;
use App;
use Illuminate\Support\Facades\Response;
use Illuminate\Routing\Controller as ControllerBase;
/**
* Rss feed builder controller
*/
class Rss
extends ControllerBase
{
/**
* Build the RSS feed action
*/
public function buildRssFeed($locale = 'en')
{
$feedster = App::make('KoderHut\RssFeedster\Feed');
return Response::make($feedster->getFeed($locale))
->header('Content-Type', $feedster->getContentType());
}
}

View File

@ -0,0 +1,97 @@
<?php
return [
'plugin' => [
'name' => 'RSSFeedster',
'description' => 'RSS Feed on-the-fly generator.',
'namespace' => 'KoderHut',
],
'settings' => [
'base' => [
'description' => 'Manage RSSFeedster base settings.',
],
'tabs' => [
'general_title' => 'General Options',
'posts_title' => 'Posts Options',
],
'fields' => [
'feed_url' => [
'label' => 'RSS Feed URL (without domain)',
'comment' => 'The URL you want assigned to the RSS feed without the install path',
'description' => '',
'default' => '/rss.xml',
],
'feed_title' => [
'label' => 'RSS Feed Title',
'comment' => 'The title of your RSS feed (can also be the site name)',
'description' => 'The title of your RSS feed (can also be the site name)',
'placeholder' => '',
'default' => 'RSS Feed Title',
],
'feed_description' => [
'label' => 'Short Description of The RSS Feed',
'comment' => 'A short description of the website that will be displayed on the feed',
'description' => 'A short description of the website that will be displayed on the feed',
],
'feed_category' => [
'label' => 'RSS Feed Category',
'comment' => 'The category element is used to specify a category for your feed.',
'description' => 'The category element makes it possible for RSS aggregators to group sites based on category.',
'placeholder' => '',
],
'feed_copyright' => [
'label' => 'RSS Feed Copyright Notice',
'comment' => 'The copyright element notifies about copyrighted material.',
'description' => 'The copyright element notifies about copyrighted material.',
'placeholder' => '',
],
'feed_language' => [
'label' => 'RSS Feed Language',
'comment' => 'The language element is used to specify the language used to write your document.',
'description' => 'The language element makes it possible for RSS aggregators to group sites based on language',
'placeholder' => '',
],
'post_max_number' => [
'label' => 'Max No. of Posts',
'comment' => 'The maximum number of posts to display into the feed. Could generate overload if the number is to high! Use -1 to disable this limit.',
'description' => 'The maximum number of posts to display into the feed. Could generate overload if the number is to high!',
],
'post_full_content' => [
'label' => 'Display Full Content',
'comment' => 'Display the full post content into the feed or an excerpt',
'description' => 'Display the full post content into the feed or an excerpt',
],
'post_page' => [
'label' => 'Post page',
'comment' => 'Set the page used to render the post',
'description' => 'Set the page used to render the post',
],
'comments_anchor' => [
'label' => 'Comments section anchor',
'comment' => 'Set ID of the element hosting the comments section',
'description' => 'Set ID of the element hosting the comments section',
'placeholder' => 'post-comments',
],
],
],
'messages' => [
'permissions' => [
'access_config_label' => 'Access Feed Configs'
],
],
];

View File

@ -0,0 +1,95 @@
<?php
/**
* Author : Denis-Florin Rendler
* Date : 21/09/15
* Copyright (c) 2015 Denis-Florin Rendler <connect@rendler.me>
*/
namespace KoderHut\RssFeedster\Models;
use Model,
Lang,
File,
Yaml as YamlParser;
use System\Classes\PluginManager;
use Cms\Classes\Page;
use KoderHut\RssFeedster\Plugin;
/**
* Settings Model
*/
class Settings
extends Model
{
use \October\Rain\Database\Traits\Validation;
/**
* @var array
*/
public $implement = ['System.Behaviors.SettingsModel'];
/**
* @var string
*/
public $settingsCode = 'koderhut_rssfeedster_settings';
/**
* @var string
*/
public $settingsFields = 'fields.yaml';
/**
* Validation rules
*/
public $rules = [
'feed_url' => 'required',
'feed_title' => 'required',
];
/**
* Init default data
*/
public function initSettingsData()
{
$this->feed_title
= Lang::get('koderhut.rssfeedster::lang.settings.fields.feed_title.default');
$this->feed_url
= Lang::get('koderhut.rssfeedster::lang.settings.fields.feed_url.default');
$this->post_page = '404';
}
/**
* Return the post_page dropdown options
*
* @return array
*/
public function getPostPageOptions()
{
return Page::sortBy('baseFileName')->lists('baseFileName', 'baseFileName');
}
/**
* Load and return the feed_language dropdown options
*
* @return array
*/
public function getFeedLanguageOptions()
{
$pluginPath = PluginManager::instance()->getPluginPath(Plugin::KODERHUT_RSSFEEDSTER_NS);
$path = $pluginPath . DIRECTORY_SEPARATOR . 'config' . DIRECTORY_SEPARATOR . 'language_codes.yaml';
if (!File::exists($path)) {
return [];
}
$languages = YamlParser::parseFile($path);
$languages = is_array($languages) ? array_flip($languages) : [];
return $languages;
}
}

View File

@ -0,0 +1,78 @@
fields:
feed_url:
tab: koderhut.rssfeedster::lang.settings.tabs.general_title
label: koderhut.rssfeedster::lang.settings.fields.feed_url.label
commentAbove: koderhut.rssfeedster::lang.settings.fields.feed_url.comment
default: '/rss.xml'
tabs:
fields:
# General tab
feed_title:
tab: koderhut.rssfeedster::lang.settings.tabs.general_title
label: koderhut.rssfeedster::lang.settings.fields.feed_title.label
placeholder: koderhut.rssfeedster::lang.settings.fields.feed_title.placeholder
comment: koderhut.rssfeedster::lang.settings.fields.feed_title.comment
description: koderhut.rssfeedster::lang.settings.fields.feed_title.description
feed_description:
tab: koderhut.rssfeedster::lang.settings.tabs.general_title
label: koderhut.rssfeedster::lang.settings.fields.feed_description.label
comment: koderhut.rssfeedster::lang.settings.fields.feed_description.comment
description: koderhut.rssfeedster::lang.settings.fields.feed_description.description
type: textarea
size: small
feed_category:
tab: koderhut.rssfeedster::lang.settings.tabs.general_title
label: koderhut.rssfeedster::lang.settings.fields.feed_category.label
placeholder: koderhut.rssfeedster::lang.settings.fields.feed_category.placeholder
comment: koderhut.rssfeedster::lang.settings.fields.feed_category.comment
description: koderhut.rssfeedster::lang.settings.fields.feed_category.description
feed_copyright:
tab: koderhut.rssfeedster::lang.settings.tabs.general_title
label: koderhut.rssfeedster::lang.settings.fields.feed_copyright.label
placeholder: koderhut.rssfeedster::lang.settings.fields.feed_copyright.placeholder
comment: koderhut.rssfeedster::lang.settings.fields.feed_copyright.comment
description: koderhut.rssfeedster::lang.settings.fields.feed_copyright.description
feed_language:
tab: koderhut.rssfeedster::lang.settings.tabs.general_title
label: koderhut.rssfeedster::lang.settings.fields.feed_language.label
placeholder: koderhut.rssfeedster::lang.settings.fields.feed_language.placeholder
comment: koderhut.rssfeedster::lang.settings.fields.feed_language.comment
description: koderhut.rssfeedster::lang.settings.fields.feed_language.description
type: dropdown
comments_anchor:
tab: koderhut.rssfeedster::lang.settings.tabs.general_title
label: koderhut.rssfeedster::lang.settings.fields.comments_anchor.label
placeholder: koderhut.rssfeedster::lang.settings.fields.comments_anchor.placeholder
comment: koderhut.rssfeedster::lang.settings.fields.comments_anchor.comment
description: koderhut.rssfeedster::lang.settings.fields.comments_anchor.description
type: text
# Posts tab
post_max_number:
tab: koderhut.rssfeedster::lang.settings.tabs.posts_title
label: koderhut.rssfeedster::lang.settings.fields.post_max_number.label
comment: koderhut.rssfeedster::lang.settings.fields.post_max_number.comment
description: koderhut.rssfeedster::lang.settings.fields.post_max_number.description
default: 20
post_full_content:
tab: koderhut.rssfeedster::lang.settings.tabs.posts_title
label: koderhut.rssfeedster::lang.settings.fields.post_full_content.label
comment: koderhut.rssfeedster::lang.settings.fields.post_full_content.comment
description: koderhut.rssfeedster::lang.settings.fields.post_full_content.description
type: checkbox
post_page:
tab: koderhut.rssfeedster::lang.settings.tabs.posts_title
label: koderhut.rssfeedster::lang.settings.fields.post_page.label
comment: koderhut.rssfeedster::lang.settings.fields.post_page.comment
description: koderhut.rssfeedster::lang.settings.fields.post_full_content.description
type: dropdown
default: 'blog/post'

View File

@ -0,0 +1,24 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit backupGlobals="false"
backupStaticAttributes="false"
bootstrap="../../../tests/bootstrap.php"
colors="true"
convertErrorsToExceptions="true"
convertNoticesToExceptions="true"
convertWarningsToExceptions="true"
processIsolation="false"
stopOnFailure="false"
syntaxCheck="false"
>
<testsuites>
<testsuite name="KoderHut.RssFeedster Unit Test Suite">
<directory>./tests</directory>
</testsuite>
</testsuites>
<php>
<env name="APP_ENV" value="testing"/>
<env name="CACHE_DRIVER" value="array"/>
<env name="SESSION_DRIVER" value="array"/>
</php>
</phpunit>

View File

@ -0,0 +1,31 @@
<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
xmlns:content="http://purl.org/rss/1.0/modules/content/"
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:atom="http://www.w3.org/2005/Atom"
>
<channel>
<title />
<link />
<atom:link href="" rel="self" type="application/rss+xml" />
<description />
<copyright />
<lastBuildDate />
<language />
<docs>http://www.rssboard.org/rss-specification</docs>
<item>
<link />
<title />
<guid isPermaLink="false" />
<pubDate />
<dc:creator />
<description />
<content:encoded />
<comments />
<category />
</item>
</channel>
</rss>

View File

@ -0,0 +1,110 @@
<?php
/**
* Author : Denis-Florin Rendler
* Date : 21/09/15
* Copyright (c) 2015 Denis-Florin Rendler <connect@rendler.me>
*/
namespace KoderHut\RssFeedster\Tests\Classes;
use App,
PluginTestCase;
use KoderHut\RssFeedster\Classes\Feedster,
KoderHut\RssFeedster\Classes\Support\Facades\DataSource,
KoderHut\RssFeedster\Classes\Support\Facades\Renderer,
KoderHut\RssFeedster\Models\Settings;
use KoderHut\RssFeedster\Tests\Mock\DataSource\MockDataSource;
use KoderHut\RssFeedster\Tests\Mock\View\MockBlankRenderer;
use KoderHut\RssFeedster\Tests\Mock\View\MockJsonRenderer;
/**
* Class FeedsterTest
* @package KoderHut\RssFeedster\Tests\Classes
*/
class FeedsterTest
extends PluginTestCase
{
/**
* Set up the test case
*/
public function setUp()
{
parent::setUp();
/**
* Create a mock-up of the Settings object
* TODO: find a better way to mock this object
*/
$config = Settings::instance();
$config->setAttribute('feed_title', 'test');
/**
* Set up the data source for the feed
*/
App::bind('KoderHut\RssFeedster\DataSource', function($app)
{
return new MockDataSource();
});
/**
* Set up the feed renderer
*/
App::bind('KoderHut\RssFeedster\Renderer', function($app)
{
$config = [
'description' => 'feed description',
'title' => 'feed title',
'category' => 'feed category',
'copyright' => 'feed copyright',
'language' => 'feed language',
'link' => 'feed url',
];
return new MockJsonRenderer($config);
});
$this->feed = new Feedster(null, null, $config);
}
/**
* Test object injection replacement
*/
public function testObjectReplacement()
{
$this->assertInstanceOf(
'KoderHut\RssFeedster\Tests\Mock\DataSource\MockDataSource',
DataSource::getFacadeRoot()
);
$this->assertInstanceOf(
'KoderHut\RssFeedster\Tests\Mock\View\MockJsonRenderer',
Renderer::getFacadeRoot()
);
}
/**
* Test that we successfully changed the renderer to
* a JSON renderer
*/
public function testJsonReplacementRenderer()
{
$this->assertJson($this->feed->getFeed());
}
/**
* Test that we successfully changed the sources
*/
public function testSources()
{
$this->feed->setRenderer(new MockBlankRenderer(['customCfgData' => 'data']));
$feedData = $this->feed->getFeed();
$this->assertArrayHasKey('customCfgData', $feedData['channel']);
$this->assertCount(3, $feedData['items']);
}
}

View File

@ -0,0 +1,224 @@
<?php
/**
* Author : Denis-Florin Rendler
* Date : 21/09/15
* Copyright (c) 2015 Denis-Florin Rendler <connect@rendler.me>
*/
namespace KoderHut\RssFeedster\Tests\Classes\View;
use Carbon\Carbon;
use KoderHut\RssFeedster\Classes\Support\Adapters\BlogPostXmlAdapter;
use PluginTestCase,
Model;
use KoderHut\RssFeedster\Classes\Contracts\IAdapter;
use KoderHut\RssFeedster\Classes\View\XmlRenderer;
/**
* Class XmlRendererTest
* @package KoderHut\RssFeedster\Tests\Classes\View
*/
class XmlRendererTest
extends PluginTestCase
{
/**
* Base feed XML structure
*
* @var string
*/
private $xmlBaseStructure = <<<XML
<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
<channel>
<description />
<title />
<category />
<copyright />
<language />
<link />
<atom:link rel="self" type="application/atom+xml" href=""/>
<generator />
<lastBuildDate />
</channel>
</rss>
XML;
/**
* Base feed XML structure for the channel tag
*
* @var string
*/
private $xmlItemStructure = <<<XML
<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
<channel>
<description />
<title />
<category />
<copyright />
<language />
<link />
<atom:link rel="self" type="application/atom+xml" href=""/>
<generator />
<lastBuildDate />
<item>
<title />
<link />
<guid />
<description />
<pubDate />
</item>
</channel>
</rss>
XML;
private $xmlTestItemStructure = <<<XML
<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
<channel>
<description />
<title />
<category />
<copyright />
<language />
<link />
<atom:link rel="self" type="application/atom+xml" href=""/>
<generator />
<lastBuildDate />
<item>
<title>post title</title>
<link><![CDATA[post url]]></link>
<guid><![CDATA[post url]]></guid>
<description><![CDATA[post summary]]></description>
<pubDate>Tue, 29-Sep-2015</pubDate>
</item>
</channel>
</rss>
XML;
/**
* Object placeholder
*
* @var null
*/
private $stub = null;
/**
* Set up the test
*/
public function setUp()
{
parent::setUp();
$adapterObj = new BlogPostXmlAdapter();
$this->stub
= $this->getMockBuilder(IAdapter::class)->getMock();
$this->stub
->method('getDataValue')
->willReturnCallback([$adapterObj, 'getDataValue']);
}
/**
* Tear down after test
*/
public function tearDown()
{
parent::tearDown();
unset($this->stub);
}
/**
* Test the XMLRenderer initial object
*/
public function testObjectBuild()
{
$xml = new XmlRenderer(
[
'description' => 'feed description',
'title' => 'feed title',
'category' => 'feed category',
'copyright' => 'feed copyright',
'language' => 'en_gb',
'link' => 'http://koderhut.eu'
],
$this->xmlBaseStructure,
$this->stub
);
$baseXmlDoc = new \DOMDocument();
$objXmlDoc = new \DOMDocument();
$objXmlDoc->loadXml($xml->renderData());
$baseXmlDoc->loadXml($this->xmlBaseStructure);
$this->assertEquals(XmlRenderer::RENDERER_CONTENT_TYPE, $xml->getContentType());
$this->assertEqualXMLStructure($baseXmlDoc->firstChild, $objXmlDoc->firstChild);
$this->assertEqualXMLStructure($baseXmlDoc->firstChild, $objXmlDoc->firstChild, true);
}
/**
* Test the XMLRenderer object with data
*/
public function testItemRender()
{
$xml = new XmlRenderer(
[
'description' => 'feed description',
'title' => 'feed title',
'category' => 'feed category',
'copyright' => 'feed copyright',
'language' => 'en_gb',
'link' => 'http://koderhut.eu'
],
$this->xmlItemStructure,
$this->stub
);
$objXmlDoc = new \DOMDocument();
$baseRssDoc = new \DOMDocument();
$objItem = false;
$baseItem = false;
$attributes = [
'title' => 'post title',
'url' => 'post url',
'summary' => 'post summary',
'published_at' => new Carbon('Tue, 29-Sep-2015'),
];
$items = [
Model::make($attributes),
Model::make($attributes),
Model::make($attributes),
];
$objXmlDoc->loadXml($xml->renderData($items));
$baseRssDoc->loadXml($this->xmlTestItemStructure);
$this->assertEqualXMLStructure(
$baseRssDoc->getElementsByTagName('item')->item(0),
$objXmlDoc->getElementsByTagName('item')->item(0)
);
$objItem = $objXmlDoc->getElementsByTagName('item')->item(0);
$baseItem = $baseRssDoc->getElementsByTagName('item')->item(0);
$this->assertTrue($objItem->hasChildNodes());
$this->assertEquals(
$baseItem->getElementsByTagName('title')->item(0)->nodeValue,
$objItem->getElementsByTagName('title')->item(0)->nodeValue
);
$this->assertEquals(
$baseItem->getElementsByTagName('link')->item(0)->nodeValue,
$objItem->getElementsByTagName('link')->item(0)->nodeValue
);
}
}

View File

@ -0,0 +1,75 @@
<?php
/**
* Author : Denis-Florin Rendler
* Date : 22/09/15
* Copyright (c) 2015 Denis-Florin Rendler <connect@rendler.me>
*/
namespace KoderHut\RssFeedster\Tests\Mock\DataSource;
use Model;
use KoderHut\RssFeedster\Classes\Contracts\IDataSource;
/**
* Class PostsSource
*
* Feed data source retrieving blog posts.
* Requires: RainLab.Blog plug-in
*
* @package KoderHut\RssFeedster\Classes\DataSource
*/
class MockDataSource
implements IDataSource
{
/**
* Cache for the data
*
* @var array|mixed
*/
protected $data = [];
/**
* Load the blog posts
*
* @return mixed
*/
public function loadData()
{
$items = [];
$attributes = [
'title' => 'post title',
'url' => 'post url',
'summary' => 'post summary',
'pubDate' => 'Tue, 29-Sep-2015',
];
if (!empty($this->data)) {
return $this->data;
}
$items = [
Model::make($attributes),
Model::make($attributes),
Model::make($attributes),
];
return $this->data = $items;
}
/**
* Retrieve posts from cache or load them and then return them
*
* @return mixed
*/
public function getData()
{
if (null !== $this->data && !empty($this->data)) {
return $this->data;
}
$this->data = $this->loadData();
return $this->data;
}
}

View File

@ -0,0 +1,64 @@
<?php
/**
* Author : Denis-Florin Rendler
* Date : 22/09/15
* Copyright (c) 2015 Denis-Florin Rendler <connect@rendler.me>
*/
namespace KoderHut\RssFeedster\Tests\Mock\View;
use KoderHut\RssFeedster\Classes\Contracts\IRenderer as Renderer;
/**
* Class MockBlankRenderer
* Build an RSS Atom valid feed xml file based on the data passed
*
* @package KoderHut\RssFeedster\Classes\View
*/
class MockBlankRenderer
implements Renderer
{
/**
* Content type header
*/
const RENDERER_CONTENT_TYPE = 'text/html';
/**
* Init method used to initialize the feed document
*
* @param array $channelData base channel data taken from settings
*/
public function __construct($channelData = [])
{
$this->data['channel'] = $channelData;
}
/**
* Build the feed items and attach them to the feed document
*
* @param array|Illuminate\Contracts\Pagination\Paginator $data
*
* @return mixed
*/
public function renderData($data = [])
{
if (empty($data)) {
return $this->data;
}
$this->data['items'] = $data;
return $this->data;
}
/**
* Return the content-type for this renderer
*
* @return string
*/
public function getContentType()
{
return self::RENDERER_CONTENT_TYPE;
}
}

View File

@ -0,0 +1,106 @@
<?php
/**
* Author : Denis-Florin Rendler
* Date : 22/09/15
* Copyright (c) 2015 Denis-Florin Rendler <connect@rendler.me>
*/
namespace KoderHut\RssFeedster\Tests\Mock\View;
use KoderHut\RssFeedster\Classes\Contracts\IRenderer as Renderer;
use KoderHut\RssFeedster\Classes\Feedster;
/**
* Class XmlRenderer
* Build an RSS Atom valid feed xml file based on the data passed
*
* @package KoderHut\RssFeedster\Classes\View
*/
class MockJsonRenderer
implements Renderer
{
/**
* Content type header
*/
const RENDERER_CONTENT_TYPE = 'application/json';
/**
* Init method used to initialize the feed document
*
* @param array $channelData base channel data taken from settings
*/
public function __construct($channelData = [])
{
$jsonDoc = new \StdClass();
$rssElement = new \StdClass();
$channelElement = new \StdClass();
$this->addChannelData($channelElement, $channelData, $jsonDoc);
$rssElement->channel = $channelElement;
$jsonDoc->rss = $rssElement;
$this->jsonDoc = $jsonDoc;
}
/**
* Build the feed items and attach them to the feed document
*
* @param array|Illuminate\Contracts\Pagination\Paginator $data
*
* @return mixed
*/
public function renderData($data = [])
{
$channel = $this->jsonDoc->rss->channel;
if (empty($data)) {
return json_encode($this->jsonDoc);
}
foreach ($data as $item) {
$jsonItem = new \StdClass();
$jsonItem->title = $item->title;
$jsonItem->link = $item->url;
$jsonItem->guid = $item->url;
$jsonItem->description = $item->description;
$jsonItem->pubDate = date('D, d-M-Y', $item->published_at);
$channelItems[] = $jsonItem;
}
$channel->item = $channelItems;
return json_encode($this->jsonDoc);
}
/**
* Return the content-type for this renderer
*
* @return string
*/
public function getContentType()
{
return self::RENDERER_CONTENT_TYPE;
}
/**
* Add the channel data
*
* @param \DomDocument $xmlDoc
* @param array $channelData
*/
protected function addChannelData($channelEl, $channelData, $jsonDoc)
{
$jsonDoc->generator = Feedster::FEED_GENERATOR_NAME;
$jsonDoc->lastBuildDate = date('D, d-M-Y');
foreach ($channelData as $element => $elValue) {
$channelEl->$element = $elValue;
}
}
}

View File

@ -0,0 +1,13 @@
1.0.1:
- First version of RssFeedster plug-in
1.0.2:
- Fix functionality bugs and plug-in description bugs
1.1.1:
- Add XML template for the RSS feed and cleanup of feed generation code
1.2.1:
- Add plug-in permissions for the configuration section
1.2.2:
- Fix typo preventing proper sorting of Posts when listing RSS feed. Thanks go to @Abhimanyu Saharan
1.2.3:
- Fix crash when installing plugin along default blog post

View File

@ -1,6 +1,7 @@
title = "Page not found (404)"
url = "/404"
layout = "default"
is_hidden = 0
==
<div class="jumbotron">
<div class="container">

View File

@ -1,10 +1,11 @@
title = "Error page (500)"
url = "/error"
layout = "default"
is_hidden = 0
==
<div class="jumbotron">
<div class="container">
<h1>Error</h1>
<p>We're sorry, but something went wrong and the page cannot be displayed.</p>
<p>We're sorry, but sometmhing went wrong and the page cannot be displayed.</p>
</div>
</div>