diff options
4 files changed, 140 insertions, 28 deletions
diff --git a/src/Wallabag/ApiBundle/Controller/WallabagRestController.php b/src/Wallabag/ApiBundle/Controller/WallabagRestController.php index 29cab1f4..744e1a60 100644 --- a/src/Wallabag/ApiBundle/Controller/WallabagRestController.php +++ b/src/Wallabag/ApiBundle/Controller/WallabagRestController.php | |||
@@ -27,13 +27,13 @@ class WallabagRestController extends FOSRestController | |||
27 | * | 27 | * |
28 | * @ApiDoc( | 28 | * @ApiDoc( |
29 | * parameters={ | 29 | * parameters={ |
30 | * {"name"="archive", "dataType"="boolean", "required"=false, "format"="true or false, all entries by default", "description"="filter by archived status."}, | 30 | * {"name"="archive", "dataType"="integer", "required"=false, "format"="1 or 0, all entries by default", "description"="filter by archived status."}, |
31 | * {"name"="star", "dataType"="boolean", "required"=false, "format"="true or false, all entries by default", "description"="filter by starred status."}, | 31 | * {"name"="starred", "dataType"="integer", "required"=false, "format"="1 or 0, all entries by default", "description"="filter by starred status."}, |
32 | * {"name"="sort", "dataType"="string", "required"=false, "format"="'created' or 'updated', default 'created'", "description"="sort entries by date."}, | 32 | * {"name"="sort", "dataType"="string", "required"=false, "format"="'created' or 'updated', default 'created'", "description"="sort entries by date."}, |
33 | * {"name"="order", "dataType"="string", "required"=false, "format"="'asc' or 'desc', default 'desc'", "description"="order of sort."}, | 33 | * {"name"="order", "dataType"="string", "required"=false, "format"="'asc' or 'desc', default 'desc'", "description"="order of sort."}, |
34 | * {"name"="page", "dataType"="integer", "required"=false, "format"="default '1'", "description"="what page you want."}, | 34 | * {"name"="page", "dataType"="integer", "required"=false, "format"="default '1'", "description"="what page you want."}, |
35 | * {"name"="perPage", "dataType"="integer", "required"=false, "format"="default'30'", "description"="results per page."}, | 35 | * {"name"="perPage", "dataType"="integer", "required"=false, "format"="default'30'", "description"="results per page."}, |
36 | * {"name"="tags", "dataType"="string", "required"=false, "format"="api%2Crest", "description"="a list of tags url encoded. Will returns entries that matches ALL tags."}, | 36 | * {"name"="tags", "dataType"="string", "required"=false, "format"="api,rest", "description"="a list of tags url encoded. Will returns entries that matches ALL tags."}, |
37 | * } | 37 | * } |
38 | * ) | 38 | * ) |
39 | * | 39 | * |
@@ -43,8 +43,8 @@ class WallabagRestController extends FOSRestController | |||
43 | { | 43 | { |
44 | $this->validateAuthentication(); | 44 | $this->validateAuthentication(); |
45 | 45 | ||
46 | $isArchived = $request->query->get('archive'); | 46 | $isArchived = (int) $request->query->get('archive'); |
47 | $isStarred = $request->query->get('star'); | 47 | $isStarred = (int) $request->query->get('starred'); |
48 | $sort = $request->query->get('sort', 'created'); | 48 | $sort = $request->query->get('sort', 'created'); |
49 | $order = $request->query->get('order', 'desc'); | 49 | $order = $request->query->get('order', 'desc'); |
50 | $page = (int) $request->query->get('page', 1); | 50 | $page = (int) $request->query->get('page', 1); |
@@ -52,7 +52,7 @@ class WallabagRestController extends FOSRestController | |||
52 | 52 | ||
53 | $pager = $this->getDoctrine() | 53 | $pager = $this->getDoctrine() |
54 | ->getRepository('WallabagCoreBundle:Entry') | 54 | ->getRepository('WallabagCoreBundle:Entry') |
55 | ->findEntries($this->getUser()->getId(), $isArchived, $isStarred, $sort, $order); | 55 | ->findEntries($this->getUser()->getId(), (bool) $isArchived, (bool) $isStarred, $sort, $order); |
56 | 56 | ||
57 | $pager->setCurrentPage($page); | 57 | $pager->setCurrentPage($page); |
58 | $pager->setMaxPerPage($perPage); | 58 | $pager->setMaxPerPage($perPage); |
@@ -97,8 +97,8 @@ class WallabagRestController extends FOSRestController | |||
97 | * {"name"="url", "dataType"="string", "required"=true, "format"="http://www.test.com/article.html", "description"="Url for the entry."}, | 97 | * {"name"="url", "dataType"="string", "required"=true, "format"="http://www.test.com/article.html", "description"="Url for the entry."}, |
98 | * {"name"="title", "dataType"="string", "required"=false, "description"="Optional, we'll get the title from the page."}, | 98 | * {"name"="title", "dataType"="string", "required"=false, "description"="Optional, we'll get the title from the page."}, |
99 | * {"name"="tags", "dataType"="string", "required"=false, "format"="tag1,tag2,tag3", "description"="a comma-separated list of tags."}, | 99 | * {"name"="tags", "dataType"="string", "required"=false, "format"="tag1,tag2,tag3", "description"="a comma-separated list of tags."}, |
100 | * {"name"="starred", "dataType"="boolean", "required"=false, "format"="true or false", "description"="entry already starred"}, | 100 | * {"name"="starred", "dataType"="integer", "required"=false, "format"="1 or 0", "description"="entry already starred"}, |
101 | * {"name"="archive", "dataType"="boolean", "required"=false, "format"="true or false", "description"="entry already archived"}, | 101 | * {"name"="archive", "dataType"="integer", "required"=false, "format"="1 or 0", "description"="entry already archived"}, |
102 | * } | 102 | * } |
103 | * ) | 103 | * ) |
104 | * | 104 | * |
@@ -109,25 +109,29 @@ class WallabagRestController extends FOSRestController | |||
109 | $this->validateAuthentication(); | 109 | $this->validateAuthentication(); |
110 | 110 | ||
111 | $url = $request->request->get('url'); | 111 | $url = $request->request->get('url'); |
112 | $isArchived = $request->request->get('archive'); | 112 | $isArchived = (int) $request->request->get('archive'); |
113 | $isStarred = $request->request->get('starred'); | 113 | $isStarred = (int) $request->request->get('starred'); |
114 | 114 | ||
115 | $entry = $this->get('wallabag_core.content_proxy')->updateEntry( | 115 | $entry = $this->get('wallabag_core.entry_repository')->findByUrlAndUserId($url, $this->getUser()->getId()); |
116 | new Entry($this->getUser()), | 116 | |
117 | $url | 117 | if (false === $entry) { |
118 | ); | 118 | $entry = $this->get('wallabag_core.content_proxy')->updateEntry( |
119 | new Entry($this->getUser()), | ||
120 | $url | ||
121 | ); | ||
122 | } | ||
119 | 123 | ||
120 | $tags = $request->request->get('tags', ''); | 124 | $tags = $request->request->get('tags', ''); |
121 | if (!empty($tags)) { | 125 | if (!empty($tags)) { |
122 | $this->get('wallabag_core.content_proxy')->assignTagsToEntry($entry, $tags); | 126 | $this->get('wallabag_core.content_proxy')->assignTagsToEntry($entry, $tags); |
123 | } | 127 | } |
124 | 128 | ||
125 | if (true === (bool) $isStarred) { | 129 | if (!is_null($isStarred)) { |
126 | $entry->setStarred(true); | 130 | $entry->setStarred((bool) $isStarred); |
127 | } | 131 | } |
128 | 132 | ||
129 | if (true === (bool) $isArchived) { | 133 | if (!is_null($isArchived)) { |
130 | $entry->setArchived(true); | 134 | $entry->setArchived((bool) $isArchived); |
131 | } | 135 | } |
132 | 136 | ||
133 | $em = $this->getDoctrine()->getManager(); | 137 | $em = $this->getDoctrine()->getManager(); |
@@ -150,8 +154,8 @@ class WallabagRestController extends FOSRestController | |||
150 | * parameters={ | 154 | * parameters={ |
151 | * {"name"="title", "dataType"="string", "required"=false}, | 155 | * {"name"="title", "dataType"="string", "required"=false}, |
152 | * {"name"="tags", "dataType"="string", "required"=false, "format"="tag1,tag2,tag3", "description"="a comma-separated list of tags."}, | 156 | * {"name"="tags", "dataType"="string", "required"=false, "format"="tag1,tag2,tag3", "description"="a comma-separated list of tags."}, |
153 | * {"name"="archive", "dataType"="boolean", "required"=false, "format"="true or false", "description"="archived the entry."}, | 157 | * {"name"="archive", "dataType"="integer", "required"=false, "format"="1 or 0", "description"="archived the entry."}, |
154 | * {"name"="star", "dataType"="boolean", "required"=false, "format"="true or false", "description"="starred the entry."}, | 158 | * {"name"="starred", "dataType"="integer", "required"=false, "format"="1 or 0", "description"="starred the entry."}, |
155 | * } | 159 | * } |
156 | * ) | 160 | * ) |
157 | * | 161 | * |
@@ -163,19 +167,19 @@ class WallabagRestController extends FOSRestController | |||
163 | $this->validateUserAccess($entry->getUser()->getId()); | 167 | $this->validateUserAccess($entry->getUser()->getId()); |
164 | 168 | ||
165 | $title = $request->request->get('title'); | 169 | $title = $request->request->get('title'); |
166 | $isArchived = $request->request->get('archive'); | 170 | $isArchived = (int) $request->request->get('archive'); |
167 | $isStarred = $request->request->get('star'); | 171 | $isStarred = (int) $request->request->get('starred'); |
168 | 172 | ||
169 | if (!is_null($title)) { | 173 | if (!is_null($title)) { |
170 | $entry->setTitle($title); | 174 | $entry->setTitle($title); |
171 | } | 175 | } |
172 | 176 | ||
173 | if (!is_null($isArchived)) { | 177 | if (!is_null($isArchived)) { |
174 | $entry->setArchived($isArchived); | 178 | $entry->setArchived((bool) $isArchived); |
175 | } | 179 | } |
176 | 180 | ||
177 | if (!is_null($isStarred)) { | 181 | if (!is_null($isStarred)) { |
178 | $entry->setStarred($isStarred); | 182 | $entry->setStarred((bool) $isStarred); |
179 | } | 183 | } |
180 | 184 | ||
181 | $tags = $request->request->get('tags', ''); | 185 | $tags = $request->request->get('tags', ''); |
diff --git a/src/Wallabag/ApiBundle/Tests/Controller/WallabagRestControllerTest.php b/src/Wallabag/ApiBundle/Tests/Controller/WallabagRestControllerTest.php index cce39d0b..ccb72d23 100644 --- a/src/Wallabag/ApiBundle/Tests/Controller/WallabagRestControllerTest.php +++ b/src/Wallabag/ApiBundle/Tests/Controller/WallabagRestControllerTest.php | |||
@@ -27,6 +27,9 @@ class WallabagRestControllerTest extends WallabagApiTestCase | |||
27 | $this->assertEquals($entry->getTitle(), $content['title']); | 27 | $this->assertEquals($entry->getTitle(), $content['title']); |
28 | $this->assertEquals($entry->getUrl(), $content['url']); | 28 | $this->assertEquals($entry->getUrl(), $content['url']); |
29 | $this->assertCount(count($entry->getTags()), $content['tags']); | 29 | $this->assertCount(count($entry->getTags()), $content['tags']); |
30 | $this->assertEquals($entry->getUserName(), $content['user_name']); | ||
31 | $this->assertEquals($entry->getUserEmail(), $content['user_email']); | ||
32 | $this->assertEquals($entry->getUserId(), $content['user_id']); | ||
30 | 33 | ||
31 | $this->assertTrue( | 34 | $this->assertTrue( |
32 | $this->client->getResponse()->headers->contains( | 35 | $this->client->getResponse()->headers->contains( |
@@ -159,6 +162,25 @@ class WallabagRestControllerTest extends WallabagApiTestCase | |||
159 | $this->assertEquals('http://www.lemonde.fr/pixels/article/2015/03/28/plongee-dans-l-univers-d-ingress-le-jeu-de-google-aux-frontieres-du-reel_4601155_4408996.html', $content['url']); | 162 | $this->assertEquals('http://www.lemonde.fr/pixels/article/2015/03/28/plongee-dans-l-univers-d-ingress-le-jeu-de-google-aux-frontieres-du-reel_4601155_4408996.html', $content['url']); |
160 | $this->assertEquals(false, $content['is_archived']); | 163 | $this->assertEquals(false, $content['is_archived']); |
161 | $this->assertEquals(false, $content['is_starred']); | 164 | $this->assertEquals(false, $content['is_starred']); |
165 | $this->assertEquals(1, $content['user_id']); | ||
166 | $this->assertCount(1, $content['tags']); | ||
167 | } | ||
168 | |||
169 | public function testPostSameEntry() | ||
170 | { | ||
171 | $this->client->request('POST', '/api/entries.json', array( | ||
172 | 'url' => 'http://www.lemonde.fr/pixels/article/2015/03/28/plongee-dans-l-univers-d-ingress-le-jeu-de-google-aux-frontieres-du-reel_4601155_4408996.html', | ||
173 | 'archive' => '1', | ||
174 | )); | ||
175 | |||
176 | $this->assertEquals(200, $this->client->getResponse()->getStatusCode()); | ||
177 | |||
178 | $content = json_decode($this->client->getResponse()->getContent(), true); | ||
179 | |||
180 | $this->assertGreaterThan(0, $content['id']); | ||
181 | $this->assertEquals('http://www.lemonde.fr/pixels/article/2015/03/28/plongee-dans-l-univers-d-ingress-le-jeu-de-google-aux-frontieres-du-reel_4601155_4408996.html', $content['url']); | ||
182 | $this->assertEquals(true, $content['is_archived']); | ||
183 | $this->assertEquals(false, $content['is_starred']); | ||
162 | $this->assertCount(1, $content['tags']); | 184 | $this->assertCount(1, $content['tags']); |
163 | } | 185 | } |
164 | 186 | ||
@@ -166,8 +188,8 @@ class WallabagRestControllerTest extends WallabagApiTestCase | |||
166 | { | 188 | { |
167 | $this->client->request('POST', '/api/entries.json', array( | 189 | $this->client->request('POST', '/api/entries.json', array( |
168 | 'url' => 'http://www.lemonde.fr/idees/article/2016/02/08/preserver-la-liberte-d-expression-sur-les-reseaux-sociaux_4861503_3232.html', | 190 | 'url' => 'http://www.lemonde.fr/idees/article/2016/02/08/preserver-la-liberte-d-expression-sur-les-reseaux-sociaux_4861503_3232.html', |
169 | 'archive' => true, | 191 | 'archive' => '1', |
170 | 'starred' => true, | 192 | 'starred' => '1', |
171 | )); | 193 | )); |
172 | 194 | ||
173 | $this->assertEquals(200, $this->client->getResponse()->getStatusCode()); | 195 | $this->assertEquals(200, $this->client->getResponse()->getStatusCode()); |
@@ -178,6 +200,25 @@ class WallabagRestControllerTest extends WallabagApiTestCase | |||
178 | $this->assertEquals('http://www.lemonde.fr/idees/article/2016/02/08/preserver-la-liberte-d-expression-sur-les-reseaux-sociaux_4861503_3232.html', $content['url']); | 200 | $this->assertEquals('http://www.lemonde.fr/idees/article/2016/02/08/preserver-la-liberte-d-expression-sur-les-reseaux-sociaux_4861503_3232.html', $content['url']); |
179 | $this->assertEquals(true, $content['is_archived']); | 201 | $this->assertEquals(true, $content['is_archived']); |
180 | $this->assertEquals(true, $content['is_starred']); | 202 | $this->assertEquals(true, $content['is_starred']); |
203 | $this->assertEquals(1, $content['user_id']); | ||
204 | } | ||
205 | |||
206 | public function testPostArchivedAndStarredEntryWithoutQuotes() | ||
207 | { | ||
208 | $this->client->request('POST', '/api/entries.json', array( | ||
209 | 'url' => 'http://www.lemonde.fr/idees/article/2016/02/08/preserver-la-liberte-d-expression-sur-les-reseaux-sociaux_4861503_3232.html', | ||
210 | 'archive' => 0, | ||
211 | 'starred' => 1, | ||
212 | )); | ||
213 | |||
214 | $this->assertEquals(200, $this->client->getResponse()->getStatusCode()); | ||
215 | |||
216 | $content = json_decode($this->client->getResponse()->getContent(), true); | ||
217 | |||
218 | $this->assertGreaterThan(0, $content['id']); | ||
219 | $this->assertEquals('http://www.lemonde.fr/idees/article/2016/02/08/preserver-la-liberte-d-expression-sur-les-reseaux-sociaux_4861503_3232.html', $content['url']); | ||
220 | $this->assertEquals(false, $content['is_archived']); | ||
221 | $this->assertEquals(true, $content['is_starred']); | ||
181 | } | 222 | } |
182 | 223 | ||
183 | public function testPatchEntry() | 224 | public function testPatchEntry() |
@@ -197,8 +238,40 @@ class WallabagRestControllerTest extends WallabagApiTestCase | |||
197 | $this->client->request('PATCH', '/api/entries/'.$entry->getId().'.json', array( | 238 | $this->client->request('PATCH', '/api/entries/'.$entry->getId().'.json', array( |
198 | 'title' => 'New awesome title', | 239 | 'title' => 'New awesome title', |
199 | 'tags' => 'new tag '.uniqid(), | 240 | 'tags' => 'new tag '.uniqid(), |
200 | 'star' => true, | 241 | 'starred' => '1', |
201 | 'archive' => false, | 242 | 'archive' => '0', |
243 | )); | ||
244 | |||
245 | $this->assertEquals(200, $this->client->getResponse()->getStatusCode()); | ||
246 | |||
247 | $content = json_decode($this->client->getResponse()->getContent(), true); | ||
248 | |||
249 | $this->assertEquals($entry->getId(), $content['id']); | ||
250 | $this->assertEquals($entry->getUrl(), $content['url']); | ||
251 | $this->assertEquals('New awesome title', $content['title']); | ||
252 | $this->assertGreaterThan($nbTags, count($content['tags'])); | ||
253 | $this->assertEquals(1, $content['user_id']); | ||
254 | } | ||
255 | |||
256 | public function testPatchEntryWithoutQuotes() | ||
257 | { | ||
258 | $entry = $this->client->getContainer() | ||
259 | ->get('doctrine.orm.entity_manager') | ||
260 | ->getRepository('WallabagCoreBundle:Entry') | ||
261 | ->findOneByUser(1); | ||
262 | |||
263 | if (!$entry) { | ||
264 | $this->markTestSkipped('No content found in db.'); | ||
265 | } | ||
266 | |||
267 | // hydrate the tags relations | ||
268 | $nbTags = count($entry->getTags()); | ||
269 | |||
270 | $this->client->request('PATCH', '/api/entries/'.$entry->getId().'.json', array( | ||
271 | 'title' => 'New awesome title', | ||
272 | 'tags' => 'new tag '.uniqid(), | ||
273 | 'starred' => 1, | ||
274 | 'archive' => 0, | ||
202 | )); | 275 | )); |
203 | 276 | ||
204 | $this->assertEquals(200, $this->client->getResponse()->getStatusCode()); | 277 | $this->assertEquals(200, $this->client->getResponse()->getStatusCode()); |
diff --git a/src/Wallabag/CoreBundle/Entity/Entry.php b/src/Wallabag/CoreBundle/Entity/Entry.php index 5e608f05..8f4ddf61 100644 --- a/src/Wallabag/CoreBundle/Entity/Entry.php +++ b/src/Wallabag/CoreBundle/Entity/Entry.php | |||
@@ -59,6 +59,8 @@ class Entry | |||
59 | /** | 59 | /** |
60 | * @var bool | 60 | * @var bool |
61 | * | 61 | * |
62 | * @Exclude | ||
63 | * | ||
62 | * @ORM\Column(name="is_archived", type="boolean") | 64 | * @ORM\Column(name="is_archived", type="boolean") |
63 | * | 65 | * |
64 | * @Groups({"entries_for_user", "export_all"}) | 66 | * @Groups({"entries_for_user", "export_all"}) |
@@ -68,6 +70,8 @@ class Entry | |||
68 | /** | 70 | /** |
69 | * @var bool | 71 | * @var bool |
70 | * | 72 | * |
73 | * @Exclude | ||
74 | * | ||
71 | * @ORM\Column(name="is_starred", type="boolean") | 75 | * @ORM\Column(name="is_starred", type="boolean") |
72 | * | 76 | * |
73 | * @Groups({"entries_for_user", "export_all"}) | 77 | * @Groups({"entries_for_user", "export_all"}) |
@@ -271,6 +275,16 @@ class Entry | |||
271 | return $this->isArchived; | 275 | return $this->isArchived; |
272 | } | 276 | } |
273 | 277 | ||
278 | /** | ||
279 | * @VirtualProperty | ||
280 | * @SerializedName("is_archived") | ||
281 | * @Groups({"entries_for_user", "export_all"}) | ||
282 | */ | ||
283 | public function is_Archived() | ||
284 | { | ||
285 | return (int) $this->isArchived(); | ||
286 | } | ||
287 | |||
274 | public function toggleArchive() | 288 | public function toggleArchive() |
275 | { | 289 | { |
276 | $this->isArchived = $this->isArchived() ^ 1; | 290 | $this->isArchived = $this->isArchived() ^ 1; |
@@ -302,6 +316,16 @@ class Entry | |||
302 | return $this->isStarred; | 316 | return $this->isStarred; |
303 | } | 317 | } |
304 | 318 | ||
319 | /** | ||
320 | * @VirtualProperty | ||
321 | * @SerializedName("is_starred") | ||
322 | * @Groups({"entries_for_user", "export_all"}) | ||
323 | */ | ||
324 | public function is_Starred() | ||
325 | { | ||
326 | return (int) $this->isStarred(); | ||
327 | } | ||
328 | |||
305 | public function toggleStar() | 329 | public function toggleStar() |
306 | { | 330 | { |
307 | $this->isStarred = $this->isStarred() ^ 1; | 331 | $this->isStarred = $this->isStarred() ^ 1; |
diff --git a/src/Wallabag/CoreBundle/Resources/public/themes/material/css/main.css b/src/Wallabag/CoreBundle/Resources/public/themes/material/css/main.css index 528c1c01..6f44e1da 100755 --- a/src/Wallabag/CoreBundle/Resources/public/themes/material/css/main.css +++ b/src/Wallabag/CoreBundle/Resources/public/themes/material/css/main.css | |||
@@ -420,6 +420,17 @@ main ul.row { | |||
420 | .reader-mode span { | 420 | .reader-mode span { |
421 | opacity: 1; | 421 | opacity: 1; |
422 | } | 422 | } |
423 | |||
424 | .tabs { | ||
425 | display: inline-block; | ||
426 | height: auto; | ||
427 | } | ||
428 | .tab { | ||
429 | min-width: 100%; | ||
430 | } | ||
431 | .indicator { | ||
432 | display: none; | ||
433 | } | ||
423 | } | 434 | } |
424 | 435 | ||
425 | @media only screen and (min-width : 400px) { | 436 | @media only screen and (min-width : 400px) { |