]> git.immae.eu Git - github/wallabag/wallabag.git/commitdiff
Changed RSS to Atom feed and improve paging
authorThomas Citharel <tcit@tcit.fr>
Tue, 13 Jun 2017 16:48:10 +0000 (18:48 +0200)
committerThomas Citharel <tcit@tcit.fr>
Sun, 9 Jul 2017 14:18:12 +0000 (16:18 +0200)
app/config/security.yml
src/Wallabag/CoreBundle/Controller/RssController.php
src/Wallabag/CoreBundle/Resources/views/themes/common/Entry/entries.xml.twig
src/Wallabag/CoreBundle/Twig/WallabagExtension.php
tests/Wallabag/CoreBundle/Controller/RssControllerTest.php
tests/Wallabag/CoreBundle/Twig/WallabagExtensionTest.php

index e14a0bd19b1924ea34d6f85e787b2c6c7c290948..5f3bf0b61e9b9485398474fcd8f4d67b35eb0d27 100644 (file)
@@ -62,6 +62,7 @@ security:
         - { 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 }
index e84044b1061da58b12ebd48779d9c84975830c3f..db0a8f9ab9a0a618dbee1e9a2f107b91d2b3caba 100644 (file)
@@ -21,40 +21,46 @@ class RssController extends Controller
     /**
      * 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);
     }
 
     /**
@@ -179,19 +185,17 @@ class RssController extends Controller
             $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'),
+        ]);
     }
 }
index d70aa5dc9c99e58fa043d47183ae5e936e0a164f..0a0131cde3c384d7d92f08c57db6ddfdb2b6ad0f 100644 (file)
@@ -1,34 +1,46 @@
 <?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>
index 351172c460f2fc24ba76c37e85e76b14ac958c57..530e0492486feae9679350438f09585cd6bcd1a5 100644 (file)
@@ -28,6 +28,7 @@ class WallabagExtension extends \Twig_Extension implements \Twig_Extension_Globa
     {
         return [
             new \Twig_SimpleFilter('removeWww', [$this, 'removeWww']),
+            new \Twig_SimpleFilter('removeScheme', [$this, 'removeScheme']),
         ];
     }
 
@@ -45,6 +46,11 @@ class WallabagExtension extends \Twig_Extension implements \Twig_Extension_Globa
         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).
      *
index 6167fe2dbf82d2a880f0f34ca5f397b181297351..0e4a5afab722264aaec324e29fa9c5d858881517 100644 (file)
@@ -12,40 +12,39 @@ class RssControllerTest extends WallabagCoreTestCase
         $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);
         }
     }
 
@@ -53,13 +52,13 @@ class RssControllerTest extends WallabagCoreTestCase
     {
         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',
             ],
         ];
     }
@@ -90,7 +89,7 @@ class RssControllerTest extends WallabagCoreTestCase
         $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());
 
@@ -112,7 +111,7 @@ class RssControllerTest extends WallabagCoreTestCase
         $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);
 
@@ -134,7 +133,7 @@ class RssControllerTest extends WallabagCoreTestCase
         $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());
 
@@ -157,16 +156,16 @@ class RssControllerTest extends WallabagCoreTestCase
 
         $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()
index ceec4b37b7a91316dcb8884cb074481d82e38606..4cb84ef152ef94423d53cbdb225c5a637048b688 100644 (file)
@@ -30,4 +30,29 @@ class WallabagExtensionTest extends \PHPUnit_Framework_TestCase
         $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'));
+    }
 }