- { path: ^/resetting, role: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: /(unread|starred|archive).xml$, roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: /tags/(.*).xml$, roles: IS_AUTHENTICATED_ANONYMOUSLY }
+ - { path: ^/feed, roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/share, roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/settings, roles: ROLE_SUPER_ADMIN }
- { path: ^/annotations, roles: ROLE_USER }
/**
* Shows unread entries for current user.
*
- * @Route("/{username}/{token}/unread.xml", name="unread_rss", defaults={"_format"="xml"})
+ * @Route("/feed/{username}/{token}/unread/{page}", name="unread_rss", defaults={"page": 1})
* @ParamConverter("user", class="WallabagUserBundle:User", converter="username_rsstoken_converter")
*
+ * @param User $user
+ * @param $page
* @return \Symfony\Component\HttpFoundation\Response
*/
- public function showUnreadRSSAction(Request $request, User $user)
+ public function showUnreadRSSAction(User $user, $page)
{
- return $this->showEntries('unread', $user, $request->query->get('page', 1));
+ return $this->showEntries('unread', $user, $page);
}
/**
* Shows read entries for current user.
*
- * @Route("/{username}/{token}/archive.xml", name="archive_rss", defaults={"_format"="xml"})
+ * @Route("/feed/{username}/{token}/archive/{page}", name="archive_rss", defaults={"page": 1})
* @ParamConverter("user", class="WallabagUserBundle:User", converter="username_rsstoken_converter")
*
+ * @param User $user
+ * @param $page
* @return \Symfony\Component\HttpFoundation\Response
*/
- public function showArchiveRSSAction(Request $request, User $user)
+ public function showArchiveRSSAction(User $user, $page)
{
- return $this->showEntries('archive', $user, $request->query->get('page', 1));
+ return $this->showEntries('archive', $user, $page);
}
/**
* Shows starred entries for current user.
*
- * @Route("/{username}/{token}/starred.xml", name="starred_rss", defaults={"_format"="xml"})
+ * @Route("/feed/{username}/{token}/starred/{page}", name="starred_rss", defaults={"page": 1})
* @ParamConverter("user", class="WallabagUserBundle:User", converter="username_rsstoken_converter")
*
+ * @param User $user
+ * @param $page
* @return \Symfony\Component\HttpFoundation\Response
*/
- public function showStarredRSSAction(Request $request, User $user)
+ public function showStarredRSSAction(User $user, $page)
{
- return $this->showEntries('starred', $user, $request->query->get('page', 1));
+ return $this->showEntries('starred', $user, $page);
}
/**
$entries->setCurrentPage((int) $page);
} catch (OutOfRangeCurrentPageException $e) {
if ($page > 1) {
- return $this->redirect($url . '?page=' . $entries->getNbPages(), 302);
+ return $this->redirect($url.'/'.$entries->getNbPages());
}
}
- return $this->render(
- '@WallabagCore/themes/common/Entry/entries.xml.twig',
- [
- 'url_html' => $this->generateUrl($type, [], UrlGeneratorInterface::ABSOLUTE_URL),
- 'type' => $type,
- 'url' => $url,
- 'entries' => $entries,
- ],
- new Response('', 200, ['Content-Type' => 'application/rss+xml'])
- );
+ return $this->render('@WallabagCore/themes/common/Entry/entries.xml.twig', [
+ 'type' => $type,
+ 'url' => $url,
+ 'entries' => $entries,
+ 'user' => $user->getUsername(),
+ 'domainName' => $this->getParameter('domain_name'),
+ 'version' => $this->getParameter('wallabag_core.version'),
+ ]);
}
}
<?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:media="http://search.yahoo.com/mrss/">
- <channel>
- <title>wallabag - {{ type }} feed</title>
- <link>{{ url_html }}</link>
- <link rel="self" href="{{ app.request.uri }}"/>
- {% if entries.hasPreviousPage -%}
- <link rel="previous" href="{{ url }}?page={{ entries.previousPage }}"/>
- {% endif -%}
- {% if entries.hasNextPage -%}
- <link rel="next" href="{{ url }}?page={{ entries.nextPage }}"/>
- {% endif -%}
- <link rel="last" href="{{ url }}?page={{ entries.nbPages }}"/>
- <pubDate>{{ "now"|date('D, d M Y H:i:s') }}</pubDate>
- <generator>wallabag</generator>
- <description>wallabag {{ type }} elements</description>
-
- {% for entry in entries %}
-
- <item>
- <title><![CDATA[{{ entry.title|e }}]]></title>
- <source url="{{ url('view', { 'id': entry.id }) }}">wallabag</source>
- <link>{{ entry.url }}</link>
- <guid>{{ entry.url }}</guid>
- <pubDate>{{ entry.createdAt|date('D, d M Y H:i:s') }}</pubDate>
- <description>
- <![CDATA[{%- if entry.readingTime > 0 -%}{{ 'entry.list.reading_time_minutes'|trans({'%readingTime%': entry.readingTime}) }}{%- else -%}{{ 'entry.list.reading_time_less_one_minute'|trans|raw }}{%- endif %}{{ entry.content|raw -}}]]>
- </description>
- </item>
-
+<feed xmlns="http://www.w3.org/2005/Atom">
+ <title>wallabag — {{type}} feed</title>
+ <subtitle type="html">RSS feed for {{ type }} entries</subtitle>
+ {% if entries | length > 0 %}
+ <updated>{{ (entries | first).createdAt | date('c') }}</updated> {# Indicates the last time the feed was modified in a significant way. #}
+ {% endif %}
+ <id>wallabag:{{ domainName | removeScheme | removeWww }}:{{ user }}:{{ type }}</id>
+ <link rel="alternate" type="text/html" href="{{ url(type) }}"/>
+ <link rel="self" type="application/atom+xml" href="{{ app.request.uri }}"/>
+ {% if entries.hasPreviousPage %}
+ <link rel="previous" href="{{ url }}/{{ entries.previousPage }}"/>
+ {% endif -%}
+ {% if entries.hasNextPage %}
+ <link rel="next" href="{{ url }}/{{ entries.nextPage }}"/>
+ {% endif -%}
+ <link rel="last" href="{{ url }}/{{ entries.nbPages }}"/>
+ <generator uri="http://wallabag.org" version="{{ version }}">wallabag</generator>
+ <author>
+ <name>{{ user }}</name>
+ </author>
+ <icon>{{ asset('favicon.ico') }}</icon>
+ <logo>{{ asset('bundles/wallabagcore/themes/_global/img/logo-square.png') }}</logo>
+ {% for entry in entries %}
+ <entry>
+ <title><![CDATA[{{ entry.title|e }}]]></title>
+ <link rel="alternate" type="text/html"
+ href="{{ url('view', {'id': entry.id}) }}"/>
+ <link rel="via">{{ entry.url }}</link>
+ <id>wallabag:{{ domainName | removeScheme | removeWww }}:{{ user }}:entry:{{ entry.id }}</id>
+ <updated>{{ entry.updatedAt|date('c') }}</updated>
+ <published>{{ entry.createdAt|date('c') }}</published>
+ {% for tag in entry.tags %}
+ <category term="{{ tag.slug }}" label="{{ tag.label }}" />
{% endfor %}
-
- </channel>
-</rss>
+ {% for author in entry.publishedBy %}
+ <author>
+ <name>{{ author }}</name>
+ </author>
+ {% endfor %}
+ <content type="html" {% if entry.language %}xml:lang="{{ entry.language[:2] }}"{% endif %}>
+ <![CDATA[{%- if entry.readingTime > 0 -%}{{ 'entry.list.reading_time_minutes'|trans({'%readingTime%': entry.readingTime}) }}{%- else -%}{{ 'entry.list.reading_time_less_one_minute'|trans|raw }}{%- endif %}{{ entry.content|raw -}}]]>
+ </content>
+ </entry>
+ {% endfor %}
+</feed>
{
return [
new \Twig_SimpleFilter('removeWww', [$this, 'removeWww']),
+ new \Twig_SimpleFilter('removeScheme', [$this, 'removeScheme']),
];
}
return preg_replace('/^www\./i', '', $url);
}
+ public function removeScheme($url)
+ {
+ return preg_replace('#^https?://#', '', $url);
+ }
+
/**
* Return number of entries depending of the type (unread, archive, starred or all).
*
$doc->loadXML($xml);
$xpath = new \DOMXpath($doc);
+ $xpath->registerNamespace('a', 'http://www.w3.org/2005/Atom');
if (null === $nb) {
- $this->assertGreaterThan(0, $xpath->query('//item')->length);
+ $this->assertGreaterThan(0, $xpath->query('//a:entry')->length);
} else {
- $this->assertSame($nb, $xpath->query('//item')->length);
+ $this->assertEquals($nb, $xpath->query('//a:entry')->length);
}
- $this->assertSame(1, $xpath->query('/rss')->length);
- $this->assertSame(1, $xpath->query('/rss/channel')->length);
+ $this->assertEquals(1, $xpath->query('/a:feed')->length);
- $this->assertSame(1, $xpath->query('/rss/channel/title')->length);
- $this->assertSame('wallabag - ' . $type . ' feed', $xpath->query('/rss/channel/title')->item(0)->nodeValue);
+ $this->assertEquals(1, $xpath->query('/a:feed/a:title')->length);
+ $this->assertEquals('wallabag — '.$type.' feed', $xpath->query('/a:feed/a:title')->item(0)->nodeValue);
- $this->assertSame(1, $xpath->query('/rss/channel/pubDate')->length);
+ $this->assertEquals(1, $xpath->query('/a:feed/a:updated')->length);
- $this->assertSame(1, $xpath->query('/rss/channel/generator')->length);
- $this->assertSame('wallabag', $xpath->query('/rss/channel/generator')->item(0)->nodeValue);
+ $this->assertEquals(1, $xpath->query('/a:feed/a:generator')->length);
+ $this->assertEquals('wallabag', $xpath->query('/a:feed/a:generator')->item(0)->nodeValue);
- $this->assertSame(1, $xpath->query('/rss/channel/description')->length);
- $this->assertSame('wallabag ' . $type . ' elements', $xpath->query('/rss/channel/description')->item(0)->nodeValue);
+ $this->assertEquals(1, $xpath->query('/a:feed/a:subtitle')->length);
+ $this->assertEquals('RSS feed for '.$type.' entries', $xpath->query('/a:feed/a:subtitle')->item(0)->nodeValue);
- $this->assertSame(1, $xpath->query('/rss/channel/link[@rel="self"]')->length);
- $this->assertContains($urlPagination . '.xml', $xpath->query('/rss/channel/link[@rel="self"]')->item(0)->getAttribute('href'));
+ $this->assertEquals(1, $xpath->query('/a:feed/a:link[@rel="self"]')->length);
+ $this->assertContains($type, $xpath->query('/a:feed/a:link[@rel="self"]')->item(0)->getAttribute('href'));
- $this->assertSame(1, $xpath->query('/rss/channel/link[@rel="last"]')->length);
- $this->assertContains($urlPagination . '.xml?page=', $xpath->query('/rss/channel/link[@rel="last"]')->item(0)->getAttribute('href'));
+ $this->assertEquals(1, $xpath->query('/a:feed/a:link[@rel="last"]')->length);
- foreach ($xpath->query('//item') as $item) {
- $this->assertSame(1, $xpath->query('title', $item)->length);
- $this->assertSame(1, $xpath->query('source', $item)->length);
- $this->assertSame(1, $xpath->query('link', $item)->length);
- $this->assertSame(1, $xpath->query('guid', $item)->length);
- $this->assertSame(1, $xpath->query('pubDate', $item)->length);
- $this->assertSame(1, $xpath->query('description', $item)->length);
+ foreach ($xpath->query('//a:entry') as $item) {
+ $this->assertEquals(1, $xpath->query('a:title', $item)->length);
+ $this->assertEquals(1, $xpath->query('a:link[@rel="via"]', $item)->length);
+ $this->assertEquals(1, $xpath->query('a:link[@rel="alternate"]', $item)->length);
+ $this->assertEquals(1, $xpath->query('a:id', $item)->length);
+ $this->assertEquals(1, $xpath->query('a:published', $item)->length);
+ $this->assertEquals(1, $xpath->query('a:content', $item)->length);
}
}
{
return [
[
- '/admin/YZIOAUZIAO/unread.xml',
+ '/feed/admin/YZIOAUZIAO/unread',
],
[
- '/wallace/YZIOAUZIAO/starred.xml',
+ '/feed/wallace/YZIOAUZIAO/starred',
],
[
- '/wallace/YZIOAUZIAO/archives.xml',
+ '/feed/wallace/YZIOAUZIAO/archives',
],
];
}
$em->persist($config);
$em->flush();
- $client->request('GET', '/admin/SUPERTOKEN/unread.xml');
+ $client->request('GET', '/feed/admin/SUPERTOKEN/unread');
$this->assertSame(200, $client->getResponse()->getStatusCode());
$em->flush();
$client = $this->getClient();
- $client->request('GET', '/admin/SUPERTOKEN/starred.xml');
+ $client->request('GET', '/feed/admin/SUPERTOKEN/starred');
$this->assertSame(200, $client->getResponse()->getStatusCode(), 1);
$em->flush();
$client = $this->getClient();
- $client->request('GET', '/admin/SUPERTOKEN/archive.xml');
+ $client->request('GET', '/feed/admin/SUPERTOKEN/archive');
$this->assertSame(200, $client->getResponse()->getStatusCode());
$client = $this->getClient();
- $client->request('GET', '/admin/SUPERTOKEN/unread.xml');
- $this->assertSame(200, $client->getResponse()->getStatusCode());
- $this->validateDom($client->getResponse()->getContent(), 'unread', 'unread');
+ $client->request('GET', '/feed/admin/SUPERTOKEN/unread');
+ $this->assertEquals(200, $client->getResponse()->getStatusCode());
+ $this->validateDom($client->getResponse()->getContent(), 'unread');
- $client->request('GET', '/admin/SUPERTOKEN/unread.xml?page=2');
- $this->assertSame(200, $client->getResponse()->getStatusCode());
- $this->validateDom($client->getResponse()->getContent(), 'unread', 'unread');
+ $client->request('GET', '/feed/admin/SUPERTOKEN/unread/2');
+ $this->assertEquals(200, $client->getResponse()->getStatusCode());
+ $this->validateDom($client->getResponse()->getContent(), 'unread');
- $client->request('GET', '/admin/SUPERTOKEN/unread.xml?page=3000');
- $this->assertSame(302, $client->getResponse()->getStatusCode());
+ $client->request('GET', '/feed/admin/SUPERTOKEN/unread/3000');
+ $this->assertEquals(302, $client->getResponse()->getStatusCode());
}
public function testTags()
$this->assertSame('lemonde.fr', $extension->removeWww('lemonde.fr'));
$this->assertSame('gist.github.com', $extension->removeWww('gist.github.com'));
}
+
+ public function testRemoveScheme()
+ {
+ $entryRepository = $this->getMockBuilder('Wallabag\CoreBundle\Repository\EntryRepository')
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $tagRepository = $this->getMockBuilder('Wallabag\CoreBundle\Repository\TagRepository')
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $tokenStorage = $this->getMockBuilder('Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface')
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $translator = $this->getMockBuilder('Symfony\Component\Translation\TranslatorInterface')
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $extension = new WallabagExtension($entryRepository, $tagRepository, $tokenStorage, 0, $translator);
+
+ $this->assertEquals('lemonde.fr', $extension->removeScheme('lemonde.fr'));
+ $this->assertEquals('gist.github.com', $extension->removeScheme('gist.github.com'));
+ $this->assertEquals('gist.github.com', $extension->removeScheme('https://gist.github.com'));
+ }
}