diff options
author | Jeremy Benoist <jeremy.benoist@gmail.com> | 2016-11-19 14:53:28 +0100 |
---|---|---|
committer | Jeremy Benoist <jeremy.benoist@gmail.com> | 2016-11-20 09:39:26 +0100 |
commit | 8670250a2674b6b388f3d40a8ba291a4dca41b82 (patch) | |
tree | 47f07b0ff474f361a5527fb2aed8d31040805c77 | |
parent | 3c969d39906dbfb9711caee9f115a1d06d06ad36 (diff) | |
download | wallabag-8670250a2674b6b388f3d40a8ba291a4dca41b82.tar.gz wallabag-8670250a2674b6b388f3d40a8ba291a4dca41b82.tar.zst wallabag-8670250a2674b6b388f3d40a8ba291a4dca41b82.zip |
Add RSS pagination
Following https://tools.ietf.org/html/rfc5005#page-4
3 files changed, 91 insertions, 12 deletions
diff --git a/src/Wallabag/CoreBundle/Controller/RssController.php b/src/Wallabag/CoreBundle/Controller/RssController.php index 38e3b5a0..2290386f 100644 --- a/src/Wallabag/CoreBundle/Controller/RssController.php +++ b/src/Wallabag/CoreBundle/Controller/RssController.php | |||
@@ -3,12 +3,15 @@ | |||
3 | namespace Wallabag\CoreBundle\Controller; | 3 | namespace Wallabag\CoreBundle\Controller; |
4 | 4 | ||
5 | use Pagerfanta\Adapter\DoctrineORMAdapter; | 5 | use Pagerfanta\Adapter\DoctrineORMAdapter; |
6 | use Pagerfanta\Exception\OutOfRangeCurrentPageException; | ||
6 | use Pagerfanta\Pagerfanta; | 7 | use Pagerfanta\Pagerfanta; |
7 | use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter; | 8 | use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter; |
8 | use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route; | 9 | use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route; |
10 | use Symfony\Component\HttpFoundation\Request; | ||
9 | use Symfony\Bundle\FrameworkBundle\Controller\Controller; | 11 | use Symfony\Bundle\FrameworkBundle\Controller\Controller; |
10 | use Wallabag\CoreBundle\Entity\Entry; | 12 | use Wallabag\CoreBundle\Entity\Entry; |
11 | use Wallabag\UserBundle\Entity\User; | 13 | use Wallabag\UserBundle\Entity\User; |
14 | use Symfony\Component\Routing\Generator\UrlGeneratorInterface; | ||
12 | 15 | ||
13 | class RssController extends Controller | 16 | class RssController extends Controller |
14 | { | 17 | { |
@@ -20,9 +23,9 @@ class RssController extends Controller | |||
20 | * | 23 | * |
21 | * @return \Symfony\Component\HttpFoundation\Response | 24 | * @return \Symfony\Component\HttpFoundation\Response |
22 | */ | 25 | */ |
23 | public function showUnreadAction(User $user) | 26 | public function showUnreadAction(Request $request, User $user) |
24 | { | 27 | { |
25 | return $this->showEntries('unread', $user); | 28 | return $this->showEntries('unread', $user, $request->query->get('page', 1)); |
26 | } | 29 | } |
27 | 30 | ||
28 | /** | 31 | /** |
@@ -33,9 +36,9 @@ class RssController extends Controller | |||
33 | * | 36 | * |
34 | * @return \Symfony\Component\HttpFoundation\Response | 37 | * @return \Symfony\Component\HttpFoundation\Response |
35 | */ | 38 | */ |
36 | public function showArchiveAction(User $user) | 39 | public function showArchiveAction(Request $request, User $user) |
37 | { | 40 | { |
38 | return $this->showEntries('archive', $user); | 41 | return $this->showEntries('archive', $user, $request->query->get('page', 1)); |
39 | } | 42 | } |
40 | 43 | ||
41 | /** | 44 | /** |
@@ -46,9 +49,9 @@ class RssController extends Controller | |||
46 | * | 49 | * |
47 | * @return \Symfony\Component\HttpFoundation\Response | 50 | * @return \Symfony\Component\HttpFoundation\Response |
48 | */ | 51 | */ |
49 | public function showStarredAction(User $user) | 52 | public function showStarredAction(Request $request, User $user) |
50 | { | 53 | { |
51 | return $this->showEntries('starred', $user); | 54 | return $this->showEntries('starred', $user, $request->query->get('page', 1)); |
52 | } | 55 | } |
53 | 56 | ||
54 | /** | 57 | /** |
@@ -57,10 +60,11 @@ class RssController extends Controller | |||
57 | * | 60 | * |
58 | * @param string $type Entries type: unread, starred or archive | 61 | * @param string $type Entries type: unread, starred or archive |
59 | * @param User $user | 62 | * @param User $user |
63 | * @param int $page | ||
60 | * | 64 | * |
61 | * @return \Symfony\Component\HttpFoundation\Response | 65 | * @return \Symfony\Component\HttpFoundation\Response |
62 | */ | 66 | */ |
63 | private function showEntries($type, User $user) | 67 | private function showEntries($type, User $user, $page = 1) |
64 | { | 68 | { |
65 | $repository = $this->getDoctrine()->getRepository('WallabagCoreBundle:Entry'); | 69 | $repository = $this->getDoctrine()->getRepository('WallabagCoreBundle:Entry'); |
66 | 70 | ||
@@ -87,8 +91,26 @@ class RssController extends Controller | |||
87 | $perPage = $user->getConfig()->getRssLimit() ?: $this->getParameter('wallabag_core.rss_limit'); | 91 | $perPage = $user->getConfig()->getRssLimit() ?: $this->getParameter('wallabag_core.rss_limit'); |
88 | $entries->setMaxPerPage($perPage); | 92 | $entries->setMaxPerPage($perPage); |
89 | 93 | ||
94 | $url = $this->generateUrl( | ||
95 | $type.'_rss', | ||
96 | [ | ||
97 | 'username' => $user->getUsername(), | ||
98 | 'token' => $user->getConfig()->getRssToken(), | ||
99 | ], | ||
100 | UrlGeneratorInterface::ABSOLUTE_URL | ||
101 | ); | ||
102 | |||
103 | try { | ||
104 | $entries->setCurrentPage((int) $page); | ||
105 | } catch (OutOfRangeCurrentPageException $e) { | ||
106 | if ($page > 1) { | ||
107 | return $this->redirect($url.'?page='.$entries->getNbPages(), 302); | ||
108 | } | ||
109 | } | ||
110 | |||
90 | return $this->render('@WallabagCore/themes/common/Entry/entries.xml.twig', [ | 111 | return $this->render('@WallabagCore/themes/common/Entry/entries.xml.twig', [ |
91 | 'type' => $type, | 112 | 'type' => $type, |
113 | 'url' => $url, | ||
92 | 'entries' => $entries, | 114 | 'entries' => $entries, |
93 | ]); | 115 | ]); |
94 | } | 116 | } |
diff --git a/src/Wallabag/CoreBundle/Resources/views/themes/common/Entry/entries.xml.twig b/src/Wallabag/CoreBundle/Resources/views/themes/common/Entry/entries.xml.twig index 288bb54f..16ecaa97 100644 --- a/src/Wallabag/CoreBundle/Resources/views/themes/common/Entry/entries.xml.twig +++ b/src/Wallabag/CoreBundle/Resources/views/themes/common/Entry/entries.xml.twig | |||
@@ -2,7 +2,15 @@ | |||
2 | <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/"> | 2 | <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/"> |
3 | <channel> | 3 | <channel> |
4 | <title>wallabag — {{type}} feed</title> | 4 | <title>wallabag — {{type}} feed</title> |
5 | <link>{{ url('unread') }}</link> | 5 | <link>{{ url(type) }}</link> |
6 | <link rel="self" href="{{ app.request.uri }}"/> | ||
7 | {% if entries.hasPreviousPage -%} | ||
8 | <link rel="previous" href="{{ url }}?page={{ entries.previousPage }}"/> | ||
9 | {% endif -%} | ||
10 | {% if entries.hasNextPage -%} | ||
11 | <link rel="next" href="{{ url }}?page={{ entries.nextPage }}"/> | ||
12 | {% endif -%} | ||
13 | <link rel="last" href="{{ url }}?page={{ entries.nbPages }}"/> | ||
6 | <pubDate>{{ "now"|date('D, d M Y H:i:s') }}</pubDate> | 14 | <pubDate>{{ "now"|date('D, d M Y H:i:s') }}</pubDate> |
7 | <generator>wallabag</generator> | 15 | <generator>wallabag</generator> |
8 | <description>wallabag {{type}} elements</description> | 16 | <description>wallabag {{type}} elements</description> |
diff --git a/tests/Wallabag/CoreBundle/Controller/RssControllerTest.php b/tests/Wallabag/CoreBundle/Controller/RssControllerTest.php index fb6fe06a..1d6c02b2 100644 --- a/tests/Wallabag/CoreBundle/Controller/RssControllerTest.php +++ b/tests/Wallabag/CoreBundle/Controller/RssControllerTest.php | |||
@@ -6,7 +6,7 @@ use Tests\Wallabag\CoreBundle\WallabagCoreTestCase; | |||
6 | 6 | ||
7 | class RssControllerTest extends WallabagCoreTestCase | 7 | class RssControllerTest extends WallabagCoreTestCase |
8 | { | 8 | { |
9 | public function validateDom($xml, $nb = null) | 9 | public function validateDom($xml, $type, $nb = null) |
10 | { | 10 | { |
11 | $doc = new \DOMDocument(); | 11 | $doc = new \DOMDocument(); |
12 | $doc->loadXML($xml); | 12 | $doc->loadXML($xml); |
@@ -22,6 +22,23 @@ class RssControllerTest extends WallabagCoreTestCase | |||
22 | $this->assertEquals(1, $xpath->query('/rss')->length); | 22 | $this->assertEquals(1, $xpath->query('/rss')->length); |
23 | $this->assertEquals(1, $xpath->query('/rss/channel')->length); | 23 | $this->assertEquals(1, $xpath->query('/rss/channel')->length); |
24 | 24 | ||
25 | $this->assertEquals(1, $xpath->query('/rss/channel/title')->length); | ||
26 | $this->assertEquals('wallabag — '.$type.' feed', $xpath->query('/rss/channel/title')->item(0)->nodeValue); | ||
27 | |||
28 | $this->assertEquals(1, $xpath->query('/rss/channel/pubDate')->length); | ||
29 | |||
30 | $this->assertEquals(1, $xpath->query('/rss/channel/generator')->length); | ||
31 | $this->assertEquals('wallabag', $xpath->query('/rss/channel/generator')->item(0)->nodeValue); | ||
32 | |||
33 | $this->assertEquals(1, $xpath->query('/rss/channel/description')->length); | ||
34 | $this->assertEquals('wallabag '.$type.' elements', $xpath->query('/rss/channel/description')->item(0)->nodeValue); | ||
35 | |||
36 | $this->assertEquals(1, $xpath->query('/rss/channel/link[@rel="self"]')->length); | ||
37 | $this->assertContains($type.'.xml', $xpath->query('/rss/channel/link[@rel="self"]')->item(0)->getAttribute('href')); | ||
38 | |||
39 | $this->assertEquals(1, $xpath->query('/rss/channel/link[@rel="last"]')->length); | ||
40 | $this->assertContains($type.'.xml?page=', $xpath->query('/rss/channel/link[@rel="last"]')->item(0)->getAttribute('href')); | ||
41 | |||
25 | foreach ($xpath->query('//item') as $item) { | 42 | foreach ($xpath->query('//item') as $item) { |
26 | $this->assertEquals(1, $xpath->query('title', $item)->length); | 43 | $this->assertEquals(1, $xpath->query('title', $item)->length); |
27 | $this->assertEquals(1, $xpath->query('source', $item)->length); | 44 | $this->assertEquals(1, $xpath->query('source', $item)->length); |
@@ -77,7 +94,7 @@ class RssControllerTest extends WallabagCoreTestCase | |||
77 | 94 | ||
78 | $this->assertEquals(200, $client->getResponse()->getStatusCode()); | 95 | $this->assertEquals(200, $client->getResponse()->getStatusCode()); |
79 | 96 | ||
80 | $this->validateDom($client->getResponse()->getContent(), 2); | 97 | $this->validateDom($client->getResponse()->getContent(), 'unread', 2); |
81 | } | 98 | } |
82 | 99 | ||
83 | public function testStarred() | 100 | public function testStarred() |
@@ -99,7 +116,7 @@ class RssControllerTest extends WallabagCoreTestCase | |||
99 | 116 | ||
100 | $this->assertEquals(200, $client->getResponse()->getStatusCode(), 1); | 117 | $this->assertEquals(200, $client->getResponse()->getStatusCode(), 1); |
101 | 118 | ||
102 | $this->validateDom($client->getResponse()->getContent()); | 119 | $this->validateDom($client->getResponse()->getContent(), 'starred'); |
103 | } | 120 | } |
104 | 121 | ||
105 | public function testArchives() | 122 | public function testArchives() |
@@ -121,6 +138,38 @@ class RssControllerTest extends WallabagCoreTestCase | |||
121 | 138 | ||
122 | $this->assertEquals(200, $client->getResponse()->getStatusCode()); | 139 | $this->assertEquals(200, $client->getResponse()->getStatusCode()); |
123 | 140 | ||
124 | $this->validateDom($client->getResponse()->getContent()); | 141 | $this->validateDom($client->getResponse()->getContent(), 'archive'); |
142 | } | ||
143 | |||
144 | public function testPagination() | ||
145 | { | ||
146 | $client = $this->getClient(); | ||
147 | $em = $client->getContainer()->get('doctrine.orm.entity_manager'); | ||
148 | $user = $em | ||
149 | ->getRepository('WallabagUserBundle:User') | ||
150 | ->findOneByUsername('admin'); | ||
151 | |||
152 | $config = $user->getConfig(); | ||
153 | $config->setRssToken('SUPERTOKEN'); | ||
154 | $config->setRssLimit(1); | ||
155 | $em->persist($config); | ||
156 | $em->flush(); | ||
157 | |||
158 | $client = $this->getClient(); | ||
159 | |||
160 | $client->request('GET', '/admin/SUPERTOKEN/archive.xml'); | ||
161 | $this->assertEquals(200, $client->getResponse()->getStatusCode()); | ||
162 | $this->validateDom($client->getResponse()->getContent(), 'archive'); | ||
163 | |||
164 | $client->request('GET', '/admin/SUPERTOKEN/archive.xml?page=2'); | ||
165 | $this->assertEquals(200, $client->getResponse()->getStatusCode()); | ||
166 | $this->validateDom($client->getResponse()->getContent(), 'archive'); | ||
167 | |||
168 | $client->request('GET', '/admin/SUPERTOKEN/archive.xml?page=3'); | ||
169 | $this->assertEquals(200, $client->getResponse()->getStatusCode()); | ||
170 | $this->validateDom($client->getResponse()->getContent(), 'archive'); | ||
171 | |||
172 | $client->request('GET', '/admin/SUPERTOKEN/archive.xml?page=3000'); | ||
173 | $this->assertEquals(302, $client->getResponse()->getStatusCode()); | ||
125 | } | 174 | } |
126 | } | 175 | } |