Browse Source

Move RssView into own plugin.

euromark 11 years ago
parent
commit
fe28b91814
4 changed files with 2 additions and 1105 deletions
  1. 1 1
      README.md
  2. 1 128
      docs/View/Rss.md
  3. 0 385
      src/View/RssView.php
  4. 0 591
      tests/TestCase/View/RssViewTest.php

+ 1 - 1
README.md

@@ -30,7 +30,7 @@ Dev (currently), Alpha, Beta, RC, 1.0 stable (incl. tagged release then).
 ### Additional features
 - The Passwordable behavior allows easy to use password functionality for frontend and backend.
 - Tree helper for working with (complex) trees and their output.
-- RSS and Ajax Views for better responses (Ajax also comes with an optional component).
+- Ajax Views for better responses (Ajax also comes with an optional component).
 - Slugged and Reset behavior
 - The Text, Time, Number libs and helpers etc provide extended functionality if desired.
 - AuthUser, Timeline, Typography, etc provide additional helper functionality.

+ 1 - 128
docs/View/Rss.md

@@ -1,130 +1,3 @@
 # Rss View
 
-A CakePHP view class to quickly output RSS feeds
-- By default template-less
-- Very customizable regarding namespaces and prefixes
-- Supports CDATA (unescaped content).
-
-## Configs
-With `setNamespace()` you can add namespaces and their definition URLs.
-They will only be added to the output if actually needed, when a namespace's prefix is being used.
-
-By default, a `rss` subdir is being used. If you don't want that, you can set the `public $subDir` to null or any other value you like.
-
-## Usage
-Enable RSS extensions with `Router::extensions(['rss'])` (in your routes or bootstrap file).
-You can then access it via:
-```
-/controller/action.rss
-```
-
-We then need an action to output our feed.
-You can enable the RSS view class it in your actions like so:
-```php
-$this->viewClass = 'Tools.Rss';
-```
-
-The nicer way would be to use extensions-routing and let CakePHP auto-switch to this view class.
-See the documentation on how to use view class mapping to automatically respond with the RssView for each request to the rss extension:
-
-    'rss' => 'Tools.Rss'
-
-With the help of parseExtensions() and RequestHandler this will save you the extra view config line in your actions.
-
-By setting the '_serialize' key in your controller, you can specify a view variable
-that should be serialized to XML and used as the response for the request.
-This allows you to omit views + layouts, if your just need to emit a single view
-variable as the XML response.
-
-In your controller, you could do the following:
-```php
-$this->set(array('posts' => $posts, '_serialize' => 'posts'));
-```
-When the view is rendered, the `$posts` view variable will be serialized
-into the RSS XML.
-
-**Note** The view variable you specify must be compatible with Xml::fromArray().
-
-If you don't use the `_serialize` key, you will need a view. You can use extended
-views to provide layout like functionality. This is currently not yet tested/supported.
-
-## Examples
-
-### Basic feed
-A basic feed contains at least a title, description and a link for both channel and items.
-It is also advised to add the `atom:link` to the location of the feed itself.
-
-```php
-$this->viewClass = 'Tools.Rss'; // Important if you do not have an auto-switch for the rss extension
-$atomLink = array('controller' => 'Topics', 'action' => 'feed', '_ext' => 'rss'); // Example controller and action
-$data = array(
-    'channel' => array(
-        'title' => 'Channel title',
-        'link' => 'http://channel.example.org',
-        'description' => 'Channel description',
-        'atom:link' => array('@href' => $atomLink),
-    ),
-    'items' => array(
-        array('title' => 'Title One', 'link' => 'http://example.org/one',
-            'author' => 'one@example.org', 'description' => 'Content one'),
-        array('title' => 'Title Two', 'link' => 'http://example.org/two',
-            'author' => 'two@example.org', 'description' => 'Content two'),
-    ));
-);
-$this->set(array('data' => $data, '_serialize' => 'data'));
-```
-
-### Built in namespaces
-It is also possible to use one of the already built in namespaces – e.g. if you want to display
-a post’s username instead of email (which you should^^). You can also add the content itself
-as CDATA. The description needs to be plain text, so if you have HTML markup, make sure to
-strip that out for the description but pass it unescaped to the content namespace tag for it.
-```php
-$data = array(
-    'channel' => array(
-        'title' => 'Channel title',
-        'link' => 'http://channel.example.org',
-        'description' => 'Channel description'
-    ),
-    'items' => array(
-        array('title' => 'Title One', 'link' => 'http://example.org/one',
-            'dc:creator' => 'Mr Bean', 'description' => 'Content one',
-            'content:encoded' => 'Some <b>HTML</b> content'),
-        array('title' => 'Title Two', 'link' => 'http://example.org/two',
-            'dc:creator' => 'Luke Skywalker', 'description' => 'Content two',
-            'content:encoded' => 'Some <b>more HTML</b> content'),
-    )
-);
-$this->set(array('data' => $data, '_serialize' => 'data'));
-```
-
-### Custom namespaces
-You can easily register new namespaces, e.g. to support the google data feeds (`xmlns:g="http://base.google.com/ns/1.0"`):
-
-```php
-$data = array(
-    'document' => array(
-        'namespace' => array(
-            'g' => 'http://base.google.com/ns/1.0'
-        )
-    )
-    'channel' => array(
-        ...
-    ),
-    'items' => array(
-        array('g:price' => 25, ...),
-    )
-);
-$this->set(array('data' => $data, '_serialize' => 'data'));
-```
-
-### Passing params.
-If you need to pass params to this view, use query strings:
-```
-.../action.rss?key1=value1&key2=value2
-```
-
-### Vista
-There are still lots of things that could be implemented. It still does not handle all the use cases possible, for example.
-
-It also stands to discussion if one could further generalize the class to not only support RSS feeds, but other type of feeds, as well.
+This has been moved to https://github.com/dereuromark/cakephp-feed

+ 0 - 385
src/View/RssView.php

@@ -1,385 +0,0 @@
-<?php
-namespace Tools\View;
-
-use Cake\Core\Configure;
-use Cake\Event\EventManager;
-use Cake\I18n\Time;
-use Cake\Network\Request;
-use Cake\Network\Response;
-use Cake\Routing\Router;
-use Cake\Utility\Hash;
-use Cake\Utility\Xml;
-use Cake\View\View;
-
-/**
- * A view class that is used for creating RSS feeds.
- *
- * By setting the '_serialize' key in your controller, you can specify a view variable
- * that should be serialized to XML and used as the response for the request.
- * This allows you to omit views + layouts, if your just need to emit a single view
- * variable as the XML response.
- *
- * In your controller, you could do the following:
- *
- * `$this->set(array('posts' => $posts, '_serialize' => 'posts'));`
- *
- * When the view is rendered, the `$posts` view variable will be serialized
- * into the RSS XML.
- *
- * **Note** The view variable you specify must be compatible with Xml::fromArray().
- *
- * If you don't use the `_serialize` key, you will need a view. You can use extended
- * views to provide layout like functionality. This is currently not yet tested/supported.
- *
- * Usage see docs.
- *
- * @author Mark Scherer
- * @license http://www.opensource.org/licenses/mit-license.php MIT License
- * @link http://www.dereuromark.de/2013/10/03/rss-feeds-in-cakephp
- */
-class RssView extends View {
-
-	/**
-	 * Default spec version of generated RSS.
-	 *
-	 * @var string
-	 */
-	public $version = '2.0';
-
-	/**
-	 * The subdirectory. RSS views are always in rss. Currently not in use.
-	 *
-	 * @var string
-	 */
-	public $subDir = 'rss';
-
-	/**
-	 * Holds usable namespaces.
-	 *
-	 * @var array
-	 * @link http://validator.w3.org/feed/docs/howto/declare_namespaces.html
-	 */
-	protected $_namespaces = array(
-		'atom' => 'http://www.w3.org/2005/Atom',
-		'content' => 'http://purl.org/rss/1.0/modules/content/',
-		'dc' => 'http://purl.org/dc/elements/1.1/',
-		'sy' => 'http://purl.org/rss/1.0/modules/syndication/'
-	);
-
-	/**
-	 * Holds the namespace keys in use.
-	 *
-	 * @var array
-	 */
-	protected $_usedNamespaces = array();
-
-	/**
-	 * Holds CDATA placeholders.
-	 *
-	 * @var array
-	 */
-	protected $_cdata = array();
-
-	/**
-	 * Constructor
-	 *
-	 * @param Controller $controller
-	 */
-	public function __construct(Request $request = null, Response $response = null,
-		EventManager $eventManager = null, array $viewOptions = []) {
-		parent::__construct($request, $response, $eventManager, $viewOptions);
-
-		if ($response && $response instanceof Response) {
-			$response->type('rss');
-		}
-	}
-
-	/**
-	 * If you are using namespaces that are not yet known to the class, you need to globablly
-	 * add them with this method. Namespaces will only be added for actually used prefixes.
-	 *
-	 * @param string $prefix
-	 * @param string $url
-	 * @return void
-	 */
-	public function setNamespace($prefix, $url) {
-		$this->_namespaces[$prefix] = $url;
-	}
-
-	/**
-	 * Prepares the channel and sets default values.
-	 *
-	 * @param array $channel
-	 * @return array Channel
-	 */
-	public function channel($channel) {
-		if (!isset($channel['link'])) {
-			$channel['link'] = '/';
-		}
-		if (!isset($channel['title'])) {
-			$channel['title'] = '';
-		}
-		if (!isset($channel['description'])) {
-			$channel['description'] = '';
-		}
-
-		$channel = $this->_prepareOutput($channel);
-		return $channel;
-	}
-
-	/**
-	 * Converts a time in any format to an RSS time
-	 *
-	 * @param int|string|DateTime $time
-	 * @return string An RSS-formatted timestamp
-	 * @see Time::toRssString()
-	 */
-	public function time($time) {
-		$time = new Time($time);
-		return $time->toRssString();
-	}
-
-	/**
-	 * Skip loading helpers if this is a _serialize based view.
-	 *
-	 * @return void
-	 */
-	public function loadHelpers() {
-		if (isset($this->viewVars['_serialize'])) {
-			return;
-		}
-		parent::loadHelpers();
-	}
-
-	/**
-	 * Render a RSS view.
-	 *
-	 * Uses the special '_serialize' parameter to convert a set of
-	 * view variables into a XML response. Makes generating simple
-	 * XML responses very easy. You can omit the '_serialize' parameter,
-	 * and use a normal view + layout as well.
-	 *
-	 * @param string $view The view being rendered.
-	 * @param string $layout The layout being rendered.
-	 * @return string The rendered view.
-	 */
-	public function render($view = null, $layout = null) {
-		if (isset($this->viewVars['_serialize'])) {
-			return $this->_serialize($this->viewVars['_serialize']);
-		}
-		if ($view !== false && $this->_getViewFileName($view)) {
-			return parent::render($view, false);
-		}
-	}
-
-	/**
-	 * Serialize view vars.
-	 *
-	 * @param string|array $serialize The viewVars that need to be serialized.
-	 * @return string The serialized data
-	 * @throws RuntimeException When the prefix is not specified
-	 */
-	protected function _serialize($serialize) {
-		$rootNode = isset($this->viewVars['_rootNode']) ? $this->viewVars['_rootNode'] : 'channel';
-
-		if (is_array($serialize)) {
-			$data = array($rootNode => array());
-			foreach ($serialize as $alias => $key) {
-				if (is_numeric($alias)) {
-					$alias = $key;
-				}
-				$data[$rootNode][$alias] = $this->viewVars[$key];
-			}
-		} else {
-			$data = isset($this->viewVars[$serialize]) ? $this->viewVars[$serialize] : null;
-			if (is_array($data) && Hash::numeric(array_keys($data))) {
-				$data = array($rootNode => array($serialize => $data));
-			}
-		}
-
-		$defaults = array('document' => array(), 'channel' => array(), 'items' => array());
-		$data += $defaults;
-		if (!empty($data['document']['namespace'])) {
-			foreach ($data['document']['namespace'] as $prefix => $url) {
-				$this->setNamespace($prefix, $url);
-			}
-		}
-
-		$channel = $this->channel($data['channel']);
-		if (!empty($channel['image']) && empty($channel['image']['title'])) {
-			$channel['image']['title'] = $channel['title'];
-		}
-
-		foreach ($data['items'] as $item) {
-			$channel['item'][] = $this->_prepareOutput($item);
-		}
-
-		$array = array(
-			'rss' => array(
-				'@version' => $this->version,
-				'channel' => $channel,
-			)
-		);
-		$namespaces = array();
-		foreach ($this->_usedNamespaces as $usedNamespacePrefix) {
-			if (!isset($this->_namespaces[$usedNamespacePrefix])) {
-				throw new \RuntimeException(sprintf('The prefix %s is not specified.', $usedNamespacePrefix));
-			}
-			$namespaces['xmlns:' . $usedNamespacePrefix] = $this->_namespaces[$usedNamespacePrefix];
-		}
-		$array['rss'] += $namespaces;
-
-		$options = array();
-		if (Configure::read('debug')) {
-			$options['pretty'] = true;
-		}
-
-		$output = Xml::fromArray($array, $options)->asXML();
-		$output = $this->_replaceCdata($output);
-
-		return $output;
-	}
-
-	/**
-	 * RssView::_prepareOutput()
-	 *
-	 * @param array $item
-	 * @return array
-	 */
-	protected function _prepareOutput($item) {
-		foreach ($item as $key => $val) {
-			$prefix = null;
-			// The cast prevents a PHP bug for switch case and false positives with integers
-			$bareKey = (string)$key;
-
-			// Detect namespaces
-			if (strpos($key, ':') !== false) {
-				list($prefix, $bareKey) = explode(':', $key, 2);
-				if (strpos($prefix, '@') !== false) {
-					$prefix = substr($prefix, 1);
-				}
-				if (!in_array($prefix, $this->_usedNamespaces)) {
-					$this->_usedNamespaces[] = $prefix;
-				}
-			}
-
-			$attrib = null;
-			switch ($bareKey) {
-				case 'encoded':
-					$val = $this->_newCdata($val);
-					break;
-
-				case 'pubDate':
-					$val = $this->time($val);
-					break;
-
-				case 'category':
-					if (is_array($val) && isset($val['domain'])) {
-						$attrib['@domain'] = $val['domain'];
-						$attrib['@'] = isset($val['content']) ? $val['content'] : $attrib['@domain'];
-						$val = $attrib;
-					} elseif (is_array($val) && !empty($val[0])) {
-						$categories = array();
-						foreach ($val as $category) {
-							$attrib = array();
-							if (is_array($category) && isset($category['domain'])) {
-								$attrib['@domain'] = $category['domain'];
-								$attrib['@'] = isset($val['content']) ? $val['content'] : $attrib['@domain'];
-								$category = $attrib;
-							}
-							$categories[] = $category;
-						}
-						$val = $categories;
-					}
-					break;
-
-				case 'link':
-				case 'url':
-				case 'guid':
-				case 'comments':
-					if (is_array($val) && isset($val['@href'])) {
-						$attrib = $val;
-						$attrib['@href'] = Router::url($val['@href'], true);
-						if ($prefix === 'atom') {
-							$attrib['@rel'] = 'self';
-							$attrib['@type'] = 'application/rss+xml';
-						}
-						$val = $attrib;
-					} elseif (is_array($val) && isset($val['url'])) {
-						$val['url'] = Router::url($val['url'], true);
-						if ($bareKey === 'guid') {
-							$val['@'] = $val['url'];
-							unset($val['url']);
-						}
-					} else {
-						$val = Router::url($val, true);
-					}
-					break;
-
-				case 'source':
-					if (is_array($val) && isset($val['url'])) {
-						$attrib['@url'] = Router::url($val['url'], true);
-						$attrib['@'] = isset($val['content']) ? $val['content'] : $attrib['@url'];
-					} elseif (!is_array($val)) {
-						$attrib['@url'] = Router::url($val, true);
-						$attrib['@'] = $attrib['@url'];
-					}
-					$val = $attrib;
-					break;
-
-				case 'enclosure':
-					if (isset($val['url']) && is_string($val['url']) && is_file(WWW_ROOT . $val['url']) && file_exists(WWW_ROOT . $val['url'])) {
-						if (!isset($val['length']) && strpos($val['url'], '://') === false) {
-							$val['length'] = sprintf("%u", filesize(WWW_ROOT . $val['url']));
-						}
-						if (!isset($val['type']) && function_exists('mime_content_type')) {
-							$val['type'] = mime_content_type(WWW_ROOT . $val['url']);
-						}
-					}
-					$attrib['@url'] = Router::url($val['url'], true);
-					$attrib['@length'] = $val['length'];
-					$attrib['@type'] = $val['type'];
-					$val = $attrib;
-					break;
-
-				default:
-					//nothing
-			}
-
-			if (is_array($val)) {
-				$val = $this->_prepareOutput($val);
-			}
-
-			$item[$key] = $val;
-		}
-
-		return $item;
-	}
-
-	/**
-	 * RssView::_newCdata()
-	 *
-	 * @param string $content
-	 * @return string
-	 */
-	protected function _newCdata($content) {
-		$i = count($this->_cdata);
-		$this->_cdata[$i] = $content;
-		return '###CDATA-' . $i . '###';
-	}
-
-	/**
-	 * RssView::_replaceCdata()
-	 *
-	 * @param string $content
-	 * @return string
-	 */
-	protected function _replaceCdata($content) {
-		foreach ($this->_cdata as $n => $data) {
-			$data = '<![CDATA[' . $data . ']]>';
-			$content = str_replace('###CDATA-' . $n . '###', $data, $content);
-		}
-		return $content;
-	}
-
-}

+ 0 - 591
tests/TestCase/View/RssViewTest.php

@@ -1,591 +0,0 @@
-<?php
-/**
- * PHP 5
- *
- * Licensed under The MIT License
- * For full copyright and license information, please see the LICENSE.txt
- * Redistributions of files must retain the above copyright notice
- *
- * @author        Mark Scherer
- * @license       http://www.opensource.org/licenses/mit-license.php MIT License
- */
-namespace Tools\Test\TestCase\View;
-
-use Cake\Routing\Router;
-use Cake\Controller\Controller;
-use Cake\Network\Request;
-use Cake\Network\Response;
-use Tools\TestSuite\TestCase;
-use Tools\View\RssView;
-
-/**
- * RssViewTest
- *
- */
-class RssViewTest extends TestCase {
-
-	public $Rss;
-
-	public $baseUrl;
-
-	/**
-	 * RssViewTest::setUp()
-	 *
-	 * @return void
-	 */
-	public function setUp() {
-		parent::setUp();
-
-		$this->Rss = new RssView();
-
-		$this->baseUrl = trim(Router::url('/', true), '/');
-	}
-
-	/**
-	 * TestTime method
-	 *
-	 * @return void
-	 */
-	public function testTime() {
-		$now = time();
-		$time = $this->Rss->time($now);
-		$this->assertEquals(date('r', $now), $time);
-	}
-
-	/**
-	 * RssViewTest::testSerialize()
-	 *
-	 * @return void
-	 */
-	public function testSerialize() {
-		$Request = new Request();
-		$Response = new Response();
-		$data = array(
-			'channel' => array(
-				'title' => 'Channel title',
-				'link' => 'http://channel.example.org',
-				'description' => 'Channel description'
-			),
-			'items' => array(
-				array('title' => 'Title One', 'link' => 'http://example.org/one',
-					'author' => 'one@example.org', 'description' => 'Content one',
-					'source' => array('url' => 'http://foo.bar')),
-				array('title' => 'Title Two', 'link' => 'http://example.org/two',
-					'author' => 'two@example.org', 'description' => 'Content two',
-					'source' => array('url' => 'http://foo.bar', 'content' => 'Foo bar')),
-			)
-		);
-		$viewVars = array('channel' => $data, '_serialize' => 'channel');
-		$View = new RssView($Request, $Response, null, ['viewVars' => $viewVars]);
-		$result = $View->render(false);
-
-		$expected = <<<RSS
-<?xml version="1.0" encoding="UTF-8"?>
-<rss version="2.0">
-  <channel>
-    <title>Channel title</title>
-    <link>http://channel.example.org</link>
-    <description>Channel description</description>
-    <item>
-      <title>Title One</title>
-      <link>http://example.org/one</link>
-      <author>one@example.org</author>
-      <description>Content one</description>
-      <source url="http://foo.bar">http://foo.bar</source>
-    </item>
-    <item>
-      <title>Title Two</title>
-      <link>http://example.org/two</link>
-      <author>two@example.org</author>
-      <description>Content two</description>
-      <source url="http://foo.bar">Foo bar</source>
-    </item>
-  </channel>
-</rss>
-
-RSS;
-		$this->assertSame('application/rss+xml', $Response->type());
-		$this->assertTextEquals($expected, $result);
-	}
-
-	/**
-	 * RssViewTest::testSerialize()
-	 *
-	 * @return void
-	 */
-	public function testSerializeWithPrefixes() {
-		$Request = new Request();
-		$Response = new Response();
-
-		$time = time();
-		$data = array(
-			'channel' => array(
-				'title' => 'Channel title',
-				'link' => 'http://channel.example.org',
-				'description' => 'Channel description',
-				'sy:updatePeriod' => 'hourly',
-				'sy:updateFrequency' => 1
-			),
-			'items' => array(
-				array('title' => 'Title One', 'link' => 'http://example.org/one',
-					'dc:creator' => 'Author One', 'pubDate' => $time),
-				array('title' => 'Title Two', 'link' => 'http://example.org/two',
-					'dc:creator' => 'Author Two', 'pubDate' => $time,
-					'source' => 'http://foo.bar'),
-			)
-		);
-		$viewVars = array('channel' => $data, '_serialize' => 'channel');
-		$View = new RssView($Request, $Response, null, ['viewVars' => $viewVars]);
-		$result = $View->render(false);
-
-		$time = date('r', $time);
-		$expected = <<<RSS
-<?xml version="1.0" encoding="UTF-8"?>
-<rss xmlns:sy="http://purl.org/rss/1.0/modules/syndication/" xmlns:dc="http://purl.org/dc/elements/1.1/" version="2.0">
-  <channel>
-    <title>Channel title</title>
-    <link>http://channel.example.org</link>
-    <description>Channel description</description>
-    <sy:updatePeriod>hourly</sy:updatePeriod>
-    <sy:updateFrequency>1</sy:updateFrequency>
-    <item>
-      <title>Title One</title>
-      <link>http://example.org/one</link>
-      <dc:creator>Author One</dc:creator>
-      <pubDate>$time</pubDate>
-    </item>
-    <item>
-      <title>Title Two</title>
-      <link>http://example.org/two</link>
-      <dc:creator>Author Two</dc:creator>
-      <pubDate>$time</pubDate>
-      <source url="http://foo.bar">http://foo.bar</source>
-    </item>
-  </channel>
-</rss>
-
-RSS;
-		$this->assertSame('application/rss+xml', $Response->type());
-		$this->assertTextEquals($expected, $result);
-	}
-
-	/**
-	 * RssViewTest::testSerializeWithUnconfiguredPrefix()
-	 *
-	 * @expectedException RuntimeException
-	 * @return void
-	 */
-	public function testSerializeWithUnconfiguredPrefix() {
-		$Request = new Request();
-		$Response = new Response();
-
-		$data = array(
-			'channel' => array(
-				'foo:bar' => 'something',
-			),
-			'items' => array(
-				array('title' => 'Title Two'),
-			)
-		);
-		$viewVars = array('channel' => $data, '_serialize' => 'channel');
-		$View = new RssView($Request, $Response, null, ['viewVars' => $viewVars]);
-		$result = $View->render(false);
-	}
-
-	/**
-	 * RssViewTest::testSerializeWithArrayLinks()
-	 *
-	 * `'atom:link' => array('@href' => array(...)` becomes
-	 * '@rel' => 'self', '@type' => 'application/rss+xml' automatically set for atom:link
-	 *
-	 * @return void
-	 */
-	public function testSerializeWithArrayLinks() {
-		$Request = new Request();
-		$Response = new Response();
-
-		$data = array(
-			'channel' => array(
-				'title' => 'Channel title',
-				'link' => 'http://channel.example.org',
-				'atom:link' => array('@href' => array('controller' => 'foo', 'action' => 'bar')),
-				'description' => 'Channel description',
-			),
-			'items' => array(
-				array('title' => 'Title One', 'link' => array('controller' => 'foo', 'action' => 'bar'), 'description' => 'Content one'),
-				array('title' => 'Title Two', 'link' => array('controller' => 'foo', 'action' => 'bar'), 'description' => 'Content two'),
-			)
-		);
-		$viewVars = array('channel' => $data, '_serialize' => 'channel');
-		$View = new RssView($Request, $Response, null, ['viewVars' => $viewVars]);
-		$result = $View->render(false);
-
-$expected = <<<RSS
-<?xml version="1.0" encoding="UTF-8"?>
-<rss xmlns:atom="http://www.w3.org/2005/Atom" version="2.0">
-  <channel>
-    <title>Channel title</title>
-    <link>http://channel.example.org</link>
-    <atom:link href="$this->baseUrl/foo/bar" rel="self" type="application/rss+xml"/>
-    <description>Channel description</description>
-    <item>
-      <title>Title One</title>
-      <link>$this->baseUrl/foo/bar</link>
-      <description>Content one</description>
-    </item>
-    <item>
-      <title>Title Two</title>
-      <link>$this->baseUrl/foo/bar</link>
-      <description>Content two</description>
-    </item>
-  </channel>
-</rss>
-
-RSS;
-		//debug($result);
-		$this->assertSame('application/rss+xml', $Response->type());
-		$this->assertTextEquals($expected, $result);
-	}
-
-	/**
-	 * RssViewTest::testSerializeWithContent()
-	 *
-	 * @return void
-	 */
-	public function testSerializeWithContent() {
-		$Request = new Request();
-		$Response = new Response();
-
-		$data = array(
-			'channel' => array(
-				'title' => 'Channel title',
-				'link' => 'http://channel.example.org',
-				'guid' => array('url' => 'http://channel.example.org', '@isPermaLink' => 'true'),
-				'atom:link' => array('@href' => array('controller' => 'foo', 'action' => 'bar')),
-			),
-			'items' => array(
-				array('title' => 'Title One', 'link' => array('controller' => 'foo', 'action' => 'bar'), 'description' => 'Content one',
-					'content:encoded' => 'HTML <img src="http://domain.com/some/link/to/image.jpg"/> <b>content</b> one'),
-				array('title' => 'Title Two', 'link' => array('controller' => 'foo', 'action' => 'bar'), 'description' => 'Content two',
-					'content:encoded' => 'HTML <img src="http://domain.com/some/link/to/image.jpg"/> <b>content</b> two'),
-			)
-		);
-		$viewVars = array('channel' => $data, '_serialize' => 'channel');
-		$View = new RssView($Request, $Response, null, ['viewVars' => $viewVars]);
-		$result = $View->render(false);
-
-		$expected = <<<RSS
-<?xml version="1.0" encoding="UTF-8"?>
-<rss xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/" version="2.0">
-  <channel>
-    <title>Channel title</title>
-    <link>http://channel.example.org</link>
-    <guid isPermaLink="true">http://channel.example.org</guid>
-    <atom:link href="$this->baseUrl/foo/bar" rel="self" type="application/rss+xml"/>
-    <description/>
-    <item>
-      <title>Title One</title>
-      <link>$this->baseUrl/foo/bar</link>
-      <description>Content one</description>
-      <content:encoded><![CDATA[HTML <img src="http://domain.com/some/link/to/image.jpg"/> <b>content</b> one]]></content:encoded>
-    </item>
-    <item>
-      <title>Title Two</title>
-      <link>$this->baseUrl/foo/bar</link>
-      <description>Content two</description>
-      <content:encoded><![CDATA[HTML <img src="http://domain.com/some/link/to/image.jpg"/> <b>content</b> two]]></content:encoded>
-    </item>
-  </channel>
-</rss>
-
-RSS;
-		//debug($output);
-		$this->assertTextEquals($expected, $result);
-	}
-
-	/**
-	 * RssViewTest::testSerializeWithCustomNamespace()
-	 *
-	 * @return void
-	 */
-	public function testSerializeWithCustomNamespace() {
-		$Request = new Request();
-		$Response = new Response();
-
-		$data = array(
-			'document' => array(
-				'namespace' => array(
-					'admin' => 'http://webns.net/mvcb/',
-					'rdf' => 'http://www.w3.org/1999/02/22-rdf-syntax-ns#'
-				)
-			),
-			'channel' => array(
-				'title' => 'Channel title',
-				'admin:errorReportsTo' => array('@rdf:resource' => 'mailto:me@example.com')
-			),
-			'items' => array(
-				array('title' => 'Title One', 'link' => array('controller' => 'foo', 'action' => 'bar')),
-			)
-		);
-		$viewVars = array('channel' => $data, '_serialize' => 'channel');
-		$View = new RssView($Request, $Response, null, ['viewVars' => $viewVars]);
-		$result = $View->render(false);
-
-		$expected = <<<RSS
-<?xml version="1.0" encoding="UTF-8"?>
-<rss xmlns:admin="http://webns.net/mvcb/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" version="2.0">
-  <channel>
-    <title>Channel title</title>
-    <admin:errorReportsTo rdf:resource="mailto:me@example.com"/>
-    <link>$this->baseUrl/</link>
-    <description/>
-    <item>
-      <title>Title One</title>
-      <link>$this->baseUrl/foo/bar</link>
-    </item>
-  </channel>
-</rss>
-
-RSS;
-		//debug($result);
-		$this->assertTextEquals($expected, $result);
-	}
-
-	/**
-	 * RssViewTest::testSerializeWithImage()
-	 *
-	 * @return void
-	 */
-	public function testSerializeWithImage() {
-		$Request = new Request();
-		$Response = new Response();
-
-		$url = array('controller' => 'topics', 'action' => 'feed', '_ext' => 'rss');
-		$data = array(
-			'channel' => array(
-				'title' => 'Channel title',
-				'guid' => array('url' => $url, '@isPermaLink' => 'true'),
-				'image' => array(
-					'url' => '/img/logo_rss.png',
-					'link' => '/'
-				)
-			),
-			'items' => array(
-				array('title' => 'Title One', 'link' => array('controller' => 'foo', 'action' => 'bar')),
-			)
-		);
-		$viewVars = array('channel' => $data, '_serialize' => 'channel');
-		$View = new RssView($Request, $Response, null, ['viewVars' => $viewVars]);
-		$result = $View->render(false);
-
-		$expected = <<<RSS
-<?xml version="1.0" encoding="UTF-8"?>
-<rss version="2.0">
-  <channel>
-    <title>Channel title</title>
-    <guid isPermaLink="true">$this->baseUrl/topics/feed.rss</guid>
-    <image>
-      <url>$this->baseUrl/img/logo_rss.png</url>
-      <link>$this->baseUrl/</link>
-      <title>Channel title</title>
-    </image>
-    <link>$this->baseUrl/</link>
-    <description/>
-    <item>
-      <title>Title One</title>
-      <link>$this->baseUrl/foo/bar</link>
-    </item>
-  </channel>
-</rss>
-
-RSS;
-		$this->assertTextEquals($expected, $result);
-	}
-
-	/**
-	 * RssViewTest::testSerializeWithCategories()
-	 *
-	 * @return void
-	 */
-	public function testSerializeWithCategories() {
-		$Request = new Request();
-		$Response = new Response();
-
-		$data = array(
-			'channel' => array(
-				'title' => 'Channel title',
-				'link' => 'http://channel.example.org',
-				'category' => 'IT/Internet/Web development & more',
-			),
-			'items' => array(
-				array('title' => 'Title One', 'link' => array('controller' => 'foo', 'action' => 'bar'), 'description' => 'Content one',
-					'category' => 'Internet'),
-				array('title' => 'Title Two', 'link' => array('controller' => 'foo', 'action' => 'bar'), 'description' => 'Content two',
-					'category' => array('News', 'Tutorial'),
-					'comments' => array('controller' => 'foo', 'action' => 'bar', '_ext' => 'rss')),
-			)
-		);
-		$viewVars = array('channel' => $data, '_serialize' => 'channel');
-		$View = new RssView($Request, $Response, null, ['viewVars' => $viewVars]);
-		$result = $View->render(false);
-
-		$expected = <<<RSS
-<?xml version="1.0" encoding="UTF-8"?>
-<rss version="2.0">
-  <channel>
-    <title>Channel title</title>
-    <link>http://channel.example.org</link>
-    <category>IT/Internet/Web development &amp; more</category>
-    <description/>
-    <item>
-      <title>Title One</title>
-      <link>$this->baseUrl/foo/bar</link>
-      <description>Content one</description>
-      <category>Internet</category>
-    </item>
-    <item>
-      <title>Title Two</title>
-      <link>$this->baseUrl/foo/bar</link>
-      <description>Content two</description>
-      <category>News</category>
-      <category>Tutorial</category>
-      <comments>$this->baseUrl/foo/bar.rss</comments>
-    </item>
-  </channel>
-</rss>
-
-RSS;
-		$this->assertTextEquals($expected, $result);
-	}
-
-	/**
-	 * RssViewTest::testSerializeWithEnclosure()
-	 *
-	 * @return void
-	 */
-	public function testSerializeWithEnclosure() {
-		$Request = new Request();
-		$Response = new Response();
-
-		$data = array(
-			'channel' => array(
-				'title' => 'Channel title',
-				'link' => 'http://channel.example.org',
-			),
-			'items' => array(
-				array('title' => 'Title One', 'link' => array('controller' => 'foo', 'action' => 'bar'), 'description' => 'Content one',
-					'enclosure' => array('url' => 'http://www.example.com/media/3d.wmv', 'length' => 78645, 'type' => 'video/wmv')),
-			)
-		);
-		$viewVars = array('channel' => $data, '_serialize' => 'channel');
-		$View = new RssView($Request, $Response, null, ['viewVars' => $viewVars]);
-		$result = $View->render(false);
-
-		$expected = <<<RSS
-<?xml version="1.0" encoding="UTF-8"?>
-<rss version="2.0">
-  <channel>
-    <title>Channel title</title>
-    <link>http://channel.example.org</link>
-    <description/>
-    <item>
-      <title>Title One</title>
-      <link>$this->baseUrl/foo/bar</link>
-      <description>Content one</description>
-      <enclosure url="http://www.example.com/media/3d.wmv" length="78645" type="video/wmv"/>
-    </item>
-  </channel>
-</rss>
-
-RSS;
-		$this->assertTextEquals($expected, $result);
-	}
-
-	/**
-	 * RssViewTest::testSerializeWithCustomTags()
-	 *
-	 * @return void
-	 */
-	public function testSerializeWithCustomTags() {
-		$Request = new Request();
-		$Response = new Response();
-
-		$data = array(
-			'channel' => array(
-				'title' => 'Channel title',
-				'link' => 'http://channel.example.org',
-			),
-			'items' => array(
-				array('title' => 'Title One', 'link' => array('controller' => 'foo', 'action' => 'bar'), 'description' => 'Content one',
-					'foo' => array('@url' => 'http://www.example.com/media/3d.wmv', '@length' => 78645, '@type' => 'video/wmv')),
-			)
-		);
-		$viewVars = array('channel' => $data, '_serialize' => 'channel');
-		$View = new RssView($Request, $Response, null, ['viewVars' => $viewVars]);
-		$result = $View->render(false);
-
-		$expected = <<<RSS
-<?xml version="1.0" encoding="UTF-8"?>
-<rss version="2.0">
-  <channel>
-    <title>Channel title</title>
-    <link>http://channel.example.org</link>
-    <description/>
-    <item>
-      <title>Title One</title>
-      <link>$this->baseUrl/foo/bar</link>
-      <description>Content one</description>
-      <foo url="http://www.example.com/media/3d.wmv" length="78645" type="video/wmv"/>
-    </item>
-  </channel>
-</rss>
-
-RSS;
-		$this->assertTextEquals($expected, $result);
-	}
-
-	/**
-	 * RssViewTest::testSerializeWithSpecialChars()
-	 *
-	 * @return void
-	 */
-	public function testSerializeWithSpecialChars() {
-		$Request = new Request();
-		$Response = new Response();
-
-		$data = array(
-			'channel' => array(
-				'title' => 'Channel title with äöü umlauts and <!> special chars',
-				'link' => 'http://channel.example.org',
-			),
-			'items' => array(
-				array(
-					'title' => 'A <unsafe title',
-					'link' => array('controller' => 'foo', 'action' => 'bar'),
-					'description' => 'My content "&" and <other> stuff here should also be escaped safely'),
-			)
-		);
-		$viewVars = array('channel' => $data, '_serialize' => 'channel');
-		$View = new RssView($Request, $Response, null, ['viewVars' => $viewVars]);
-		$result = $View->render(false);
-
-		$expected = <<<RSS
-<?xml version="1.0" encoding="UTF-8"?>
-<rss version="2.0">
-  <channel>
-    <title>Channel title with äöü umlauts and &lt;!&gt; special chars</title>
-    <link>http://channel.example.org</link>
-    <description/>
-    <item>
-      <title>A &lt;unsafe title</title>
-      <link>$this->baseUrl/foo/bar</link>
-      <description>My content "&amp;" and &lt;other&gt; stuff here should also be escaped safely</description>
-    </item>
-  </channel>
-</rss>
-
-RSS;
-		$this->assertTextEquals($expected, $result);
-	}
-
-}