aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/Wallabag/ApiBundle
diff options
context:
space:
mode:
authorJeremy Benoist <jeremy.benoist@gmail.com>2016-11-03 16:41:29 +0100
committerJeremy Benoist <jeremy.benoist@gmail.com>2016-11-03 16:41:29 +0100
commit5a619812ca3eb05a82a023ccdaee13501eb8d45f (patch)
treea1541999a3e13f9bb8b45d3a61320ee61aa4eb3c /src/Wallabag/ApiBundle
parentda4136557963018287cae61226e9006c3c741747 (diff)
parent84795d015b3c7e1af48a3dda3cb33cf080b66e8f (diff)
downloadwallabag-5a619812ca3eb05a82a023ccdaee13501eb8d45f.tar.gz
wallabag-5a619812ca3eb05a82a023ccdaee13501eb8d45f.tar.zst
wallabag-5a619812ca3eb05a82a023ccdaee13501eb8d45f.zip
Merge remote-tracking branch 'origin/master' into 2.2
Diffstat (limited to 'src/Wallabag/ApiBundle')
-rw-r--r--src/Wallabag/ApiBundle/Controller/EntryRestController.php374
-rw-r--r--src/Wallabag/ApiBundle/Controller/TagRestController.php171
-rw-r--r--src/Wallabag/ApiBundle/Controller/WallabagRestController.php641
-rw-r--r--src/Wallabag/ApiBundle/Resources/config/routing_rest.yml18
4 files changed, 563 insertions, 641 deletions
diff --git a/src/Wallabag/ApiBundle/Controller/EntryRestController.php b/src/Wallabag/ApiBundle/Controller/EntryRestController.php
new file mode 100644
index 00000000..b3622c62
--- /dev/null
+++ b/src/Wallabag/ApiBundle/Controller/EntryRestController.php
@@ -0,0 +1,374 @@
1<?php
2
3namespace Wallabag\ApiBundle\Controller;
4
5use Hateoas\Configuration\Route;
6use Hateoas\Representation\Factory\PagerfantaFactory;
7use Nelmio\ApiDocBundle\Annotation\ApiDoc;
8use Symfony\Component\HttpFoundation\Request;
9use Symfony\Component\HttpFoundation\JsonResponse;
10use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
11use Wallabag\CoreBundle\Entity\Entry;
12use Wallabag\CoreBundle\Entity\Tag;
13use Wallabag\CoreBundle\Event\EntrySavedEvent;
14use Wallabag\CoreBundle\Event\EntryDeletedEvent;
15
16class EntryRestController extends WallabagRestController
17{
18 /**
19 * Check if an entry exist by url.
20 *
21 * @ApiDoc(
22 * parameters={
23 * {"name"="url", "dataType"="string", "required"=true, "format"="An url", "description"="Url to check if it exists"},
24 * {"name"="urls", "dataType"="string", "required"=false, "format"="An array of urls (?urls[]=http...&urls[]=http...)", "description"="Urls (as an array) to check if it exists"}
25 * }
26 * )
27 *
28 * @return JsonResponse
29 */
30 public function getEntriesExistsAction(Request $request)
31 {
32 $this->validateAuthentication();
33
34 $urls = $request->query->get('urls', []);
35
36 // handle multiple urls first
37 if (!empty($urls)) {
38 $results = [];
39 foreach ($urls as $url) {
40 $res = $this->getDoctrine()
41 ->getRepository('WallabagCoreBundle:Entry')
42 ->findByUrlAndUserId($url, $this->getUser()->getId());
43
44 $results[$url] = false === $res ? false : true;
45 }
46
47 $json = $this->get('serializer')->serialize($results, 'json');
48
49 return (new JsonResponse())->setJson($json);
50 }
51
52 // let's see if it is a simple url?
53 $url = $request->query->get('url', '');
54
55 if (empty($url)) {
56 throw $this->createAccessDeniedException('URL is empty?, logged user id: '.$this->getUser()->getId());
57 }
58
59 $res = $this->getDoctrine()
60 ->getRepository('WallabagCoreBundle:Entry')
61 ->findByUrlAndUserId($url, $this->getUser()->getId());
62
63 $exists = false === $res ? false : true;
64
65 $json = $this->get('serializer')->serialize(['exists' => $exists], 'json');
66
67 return (new JsonResponse())->setJson($json);
68 }
69
70 /**
71 * Retrieve all entries. It could be filtered by many options.
72 *
73 * @ApiDoc(
74 * parameters={
75 * {"name"="archive", "dataType"="integer", "required"=false, "format"="1 or 0, all entries by default", "description"="filter by archived status."},
76 * {"name"="starred", "dataType"="integer", "required"=false, "format"="1 or 0, all entries by default", "description"="filter by starred status."},
77 * {"name"="sort", "dataType"="string", "required"=false, "format"="'created' or 'updated', default 'created'", "description"="sort entries by date."},
78 * {"name"="order", "dataType"="string", "required"=false, "format"="'asc' or 'desc', default 'desc'", "description"="order of sort."},
79 * {"name"="page", "dataType"="integer", "required"=false, "format"="default '1'", "description"="what page you want."},
80 * {"name"="perPage", "dataType"="integer", "required"=false, "format"="default'30'", "description"="results per page."},
81 * {"name"="tags", "dataType"="string", "required"=false, "format"="api,rest", "description"="a list of tags url encoded. Will returns entries that matches ALL tags."},
82 * {"name"="since", "dataType"="integer", "required"=false, "format"="default '0'", "description"="The timestamp since when you want entries updated."},
83 * }
84 * )
85 *
86 * @return JsonResponse
87 */
88 public function getEntriesAction(Request $request)
89 {
90 $this->validateAuthentication();
91
92 $isArchived = (null === $request->query->get('archive')) ? null : (bool) $request->query->get('archive');
93 $isStarred = (null === $request->query->get('starred')) ? null : (bool) $request->query->get('starred');
94 $sort = $request->query->get('sort', 'created');
95 $order = $request->query->get('order', 'desc');
96 $page = (int) $request->query->get('page', 1);
97 $perPage = (int) $request->query->get('perPage', 30);
98 $tags = $request->query->get('tags', '');
99 $since = $request->query->get('since', 0);
100
101 $pager = $this->getDoctrine()
102 ->getRepository('WallabagCoreBundle:Entry')
103 ->findEntries($this->getUser()->getId(), $isArchived, $isStarred, $sort, $order, $since, $tags);
104
105 $pager->setCurrentPage($page);
106 $pager->setMaxPerPage($perPage);
107
108 $pagerfantaFactory = new PagerfantaFactory('page', 'perPage');
109 $paginatedCollection = $pagerfantaFactory->createRepresentation(
110 $pager,
111 new Route(
112 'api_get_entries',
113 [
114 'archive' => $isArchived,
115 'starred' => $isStarred,
116 'sort' => $sort,
117 'order' => $order,
118 'page' => $page,
119 'perPage' => $perPage,
120 'tags' => $tags,
121 'since' => $since,
122 ],
123 UrlGeneratorInterface::ABSOLUTE_URL
124 )
125 );
126
127 $json = $this->get('serializer')->serialize($paginatedCollection, 'json');
128
129 return (new JsonResponse())->setJson($json);
130 }
131
132 /**
133 * Retrieve a single entry.
134 *
135 * @ApiDoc(
136 * requirements={
137 * {"name"="entry", "dataType"="integer", "requirement"="\w+", "description"="The entry ID"}
138 * }
139 * )
140 *
141 * @return JsonResponse
142 */
143 public function getEntryAction(Entry $entry)
144 {
145 $this->validateAuthentication();
146 $this->validateUserAccess($entry->getUser()->getId());
147
148 $json = $this->get('serializer')->serialize($entry, 'json');
149
150 return (new JsonResponse())->setJson($json);
151 }
152
153 /**
154 * Create an entry.
155 *
156 * @ApiDoc(
157 * parameters={
158 * {"name"="url", "dataType"="string", "required"=true, "format"="http://www.test.com/article.html", "description"="Url for the entry."},
159 * {"name"="title", "dataType"="string", "required"=false, "description"="Optional, we'll get the title from the page."},
160 * {"name"="tags", "dataType"="string", "required"=false, "format"="tag1,tag2,tag3", "description"="a comma-separated list of tags."},
161 * {"name"="starred", "dataType"="integer", "required"=false, "format"="1 or 0", "description"="entry already starred"},
162 * {"name"="archive", "dataType"="integer", "required"=false, "format"="1 or 0", "description"="entry already archived"},
163 * }
164 * )
165 *
166 * @return JsonResponse
167 */
168 public function postEntriesAction(Request $request)
169 {
170 $this->validateAuthentication();
171
172 $url = $request->request->get('url');
173 $title = $request->request->get('title');
174 $isArchived = $request->request->get('archive');
175 $isStarred = $request->request->get('starred');
176
177 $entry = $this->get('wallabag_core.entry_repository')->findByUrlAndUserId($url, $this->getUser()->getId());
178
179 if (false === $entry) {
180 $entry = $this->get('wallabag_core.content_proxy')->updateEntry(
181 new Entry($this->getUser()),
182 $url
183 );
184 }
185
186 if (!is_null($title)) {
187 $entry->setTitle($title);
188 }
189
190 $tags = $request->request->get('tags', '');
191 if (!empty($tags)) {
192 $this->get('wallabag_core.content_proxy')->assignTagsToEntry($entry, $tags);
193 }
194
195 if (!is_null($isStarred)) {
196 $entry->setStarred((bool) $isStarred);
197 }
198
199 if (!is_null($isArchived)) {
200 $entry->setArchived((bool) $isArchived);
201 }
202
203 $em = $this->getDoctrine()->getManager();
204 $em->persist($entry);
205 $em->flush();
206
207 // entry saved, dispatch event about it!
208 $this->get('event_dispatcher')->dispatch(EntrySavedEvent::NAME, new EntrySavedEvent($entry));
209
210 $json = $this->get('serializer')->serialize($entry, 'json');
211
212 return (new JsonResponse())->setJson($json);
213 }
214
215 /**
216 * Change several properties of an entry.
217 *
218 * @ApiDoc(
219 * requirements={
220 * {"name"="entry", "dataType"="integer", "requirement"="\w+", "description"="The entry ID"}
221 * },
222 * parameters={
223 * {"name"="title", "dataType"="string", "required"=false},
224 * {"name"="tags", "dataType"="string", "required"=false, "format"="tag1,tag2,tag3", "description"="a comma-separated list of tags."},
225 * {"name"="archive", "dataType"="integer", "required"=false, "format"="1 or 0", "description"="archived the entry."},
226 * {"name"="starred", "dataType"="integer", "required"=false, "format"="1 or 0", "description"="starred the entry."},
227 * }
228 * )
229 *
230 * @return JsonResponse
231 */
232 public function patchEntriesAction(Entry $entry, Request $request)
233 {
234 $this->validateAuthentication();
235 $this->validateUserAccess($entry->getUser()->getId());
236
237 $title = $request->request->get('title');
238 $isArchived = $request->request->get('archive');
239 $isStarred = $request->request->get('starred');
240
241 if (!is_null($title)) {
242 $entry->setTitle($title);
243 }
244
245 if (!is_null($isArchived)) {
246 $entry->setArchived((bool) $isArchived);
247 }
248
249 if (!is_null($isStarred)) {
250 $entry->setStarred((bool) $isStarred);
251 }
252
253 $tags = $request->request->get('tags', '');
254 if (!empty($tags)) {
255 $this->get('wallabag_core.content_proxy')->assignTagsToEntry($entry, $tags);
256 }
257
258 $em = $this->getDoctrine()->getManager();
259 $em->flush();
260
261 $json = $this->get('serializer')->serialize($entry, 'json');
262
263 return (new JsonResponse())->setJson($json);
264 }
265
266 /**
267 * Delete **permanently** an entry.
268 *
269 * @ApiDoc(
270 * requirements={
271 * {"name"="entry", "dataType"="integer", "requirement"="\w+", "description"="The entry ID"}
272 * }
273 * )
274 *
275 * @return JsonResponse
276 */
277 public function deleteEntriesAction(Entry $entry)
278 {
279 $this->validateAuthentication();
280 $this->validateUserAccess($entry->getUser()->getId());
281
282 $em = $this->getDoctrine()->getManager();
283 $em->remove($entry);
284 $em->flush();
285
286 // entry deleted, dispatch event about it!
287 $this->get('event_dispatcher')->dispatch(EntryDeletedEvent::NAME, new EntryDeletedEvent($entry));
288
289 $json = $this->get('serializer')->serialize($entry, 'json');
290
291 return (new JsonResponse())->setJson($json);
292 }
293
294 /**
295 * Retrieve all tags for an entry.
296 *
297 * @ApiDoc(
298 * requirements={
299 * {"name"="entry", "dataType"="integer", "requirement"="\w+", "description"="The entry ID"}
300 * }
301 * )
302 *
303 * @return JsonResponse
304 */
305 public function getEntriesTagsAction(Entry $entry)
306 {
307 $this->validateAuthentication();
308 $this->validateUserAccess($entry->getUser()->getId());
309
310 $json = $this->get('serializer')->serialize($entry->getTags(), 'json');
311
312 return (new JsonResponse())->setJson($json);
313 }
314
315 /**
316 * Add one or more tags to an entry.
317 *
318 * @ApiDoc(
319 * requirements={
320 * {"name"="entry", "dataType"="integer", "requirement"="\w+", "description"="The entry ID"}
321 * },
322 * parameters={
323 * {"name"="tags", "dataType"="string", "required"=false, "format"="tag1,tag2,tag3", "description"="a comma-separated list of tags."},
324 * }
325 * )
326 *
327 * @return JsonResponse
328 */
329 public function postEntriesTagsAction(Request $request, Entry $entry)
330 {
331 $this->validateAuthentication();
332 $this->validateUserAccess($entry->getUser()->getId());
333
334 $tags = $request->request->get('tags', '');
335 if (!empty($tags)) {
336 $this->get('wallabag_core.content_proxy')->assignTagsToEntry($entry, $tags);
337 }
338
339 $em = $this->getDoctrine()->getManager();
340 $em->persist($entry);
341 $em->flush();
342
343 $json = $this->get('serializer')->serialize($entry, 'json');
344
345 return (new JsonResponse())->setJson($json);
346 }
347
348 /**
349 * Permanently remove one tag for an entry.
350 *
351 * @ApiDoc(
352 * requirements={
353 * {"name"="tag", "dataType"="integer", "requirement"="\w+", "description"="The tag ID"},
354 * {"name"="entry", "dataType"="integer", "requirement"="\w+", "description"="The entry ID"}
355 * }
356 * )
357 *
358 * @return JsonResponse
359 */
360 public function deleteEntriesTagsAction(Entry $entry, Tag $tag)
361 {
362 $this->validateAuthentication();
363 $this->validateUserAccess($entry->getUser()->getId());
364
365 $entry->removeTag($tag);
366 $em = $this->getDoctrine()->getManager();
367 $em->persist($entry);
368 $em->flush();
369
370 $json = $this->get('serializer')->serialize($entry, 'json');
371
372 return (new JsonResponse())->setJson($json);
373 }
374}
diff --git a/src/Wallabag/ApiBundle/Controller/TagRestController.php b/src/Wallabag/ApiBundle/Controller/TagRestController.php
new file mode 100644
index 00000000..4e7ddc66
--- /dev/null
+++ b/src/Wallabag/ApiBundle/Controller/TagRestController.php
@@ -0,0 +1,171 @@
1<?php
2
3namespace Wallabag\ApiBundle\Controller;
4
5use Nelmio\ApiDocBundle\Annotation\ApiDoc;
6use Symfony\Component\HttpFoundation\Request;
7use Symfony\Component\HttpFoundation\JsonResponse;
8use Wallabag\CoreBundle\Entity\Entry;
9use Wallabag\CoreBundle\Entity\Tag;
10
11class TagRestController extends WallabagRestController
12{
13 /**
14 * Retrieve all tags.
15 *
16 * @ApiDoc()
17 *
18 * @return JsonResponse
19 */
20 public function getTagsAction()
21 {
22 $this->validateAuthentication();
23
24 $tags = $this->getDoctrine()
25 ->getRepository('WallabagCoreBundle:Tag')
26 ->findAllTags($this->getUser()->getId());
27
28 $json = $this->get('serializer')->serialize($tags, 'json');
29
30 return (new JsonResponse())->setJson($json);
31 }
32
33 /**
34 * Permanently remove one tag from **every** entry.
35 *
36 * @ApiDoc(
37 * requirements={
38 * {"name"="tag", "dataType"="string", "required"=true, "requirement"="\w+", "description"="Tag as a string"}
39 * }
40 * )
41 *
42 * @return JsonResponse
43 */
44 public function deleteTagLabelAction(Request $request)
45 {
46 $this->validateAuthentication();
47 $label = $request->request->get('tag', '');
48
49 $tag = $this->getDoctrine()->getRepository('WallabagCoreBundle:Tag')->findOneByLabel($label);
50
51 if (empty($tag)) {
52 throw $this->createNotFoundException('Tag not found');
53 }
54
55 $this->getDoctrine()
56 ->getRepository('WallabagCoreBundle:Entry')
57 ->removeTag($this->getUser()->getId(), $tag);
58
59 $this->cleanOrphanTag($tag);
60
61 $json = $this->get('serializer')->serialize($tag, 'json');
62
63 return (new JsonResponse())->setJson($json);
64 }
65
66 /**
67 * Permanently remove some tags from **every** entry.
68 *
69 * @ApiDoc(
70 * requirements={
71 * {"name"="tags", "dataType"="string", "required"=true, "format"="tag1,tag2", "description"="Tags as strings (comma splitted)"}
72 * }
73 * )
74 *
75 * @return JsonResponse
76 */
77 public function deleteTagsLabelAction(Request $request)
78 {
79 $this->validateAuthentication();
80
81 $tagsLabels = $request->request->get('tags', '');
82
83 $tags = [];
84
85 foreach (explode(',', $tagsLabels) as $tagLabel) {
86 $tagEntity = $this->getDoctrine()->getRepository('WallabagCoreBundle:Tag')->findOneByLabel($tagLabel);
87
88 if (!empty($tagEntity)) {
89 $tags[] = $tagEntity;
90 }
91 }
92
93 if (empty($tags)) {
94 throw $this->createNotFoundException('Tags not found');
95 }
96
97 $this->getDoctrine()
98 ->getRepository('WallabagCoreBundle:Entry')
99 ->removeTags($this->getUser()->getId(), $tags);
100
101 $this->cleanOrphanTag($tags);
102
103 $json = $this->get('serializer')->serialize($tags, 'json');
104
105 return (new JsonResponse())->setJson($json);
106 }
107
108 /**
109 * Permanently remove one tag from **every** entry.
110 *
111 * @ApiDoc(
112 * requirements={
113 * {"name"="tag", "dataType"="integer", "requirement"="\w+", "description"="The tag"}
114 * }
115 * )
116 *
117 * @return JsonResponse
118 */
119 public function deleteTagAction(Tag $tag)
120 {
121 $this->validateAuthentication();
122
123 $this->getDoctrine()
124 ->getRepository('WallabagCoreBundle:Entry')
125 ->removeTag($this->getUser()->getId(), $tag);
126
127 $this->cleanOrphanTag($tag);
128
129 $json = $this->get('serializer')->serialize($tag, 'json');
130
131 return (new JsonResponse())->setJson($json);
132 }
133
134 /**
135 * Retrieve version number.
136 *
137 * @ApiDoc()
138 *
139 * @return JsonResponse
140 */
141 public function getVersionAction()
142 {
143 $version = $this->container->getParameter('wallabag_core.version');
144
145 $json = $this->get('serializer')->serialize($version, 'json');
146
147 return (new JsonResponse())->setJson($json);
148 }
149
150 /**
151 * Remove orphan tag in case no entries are associated to it.
152 *
153 * @param Tag|array $tags
154 */
155 private function cleanOrphanTag($tags)
156 {
157 if (!is_array($tags)) {
158 $tags = [$tags];
159 }
160
161 $em = $this->getDoctrine()->getManager();
162
163 foreach ($tags as $tag) {
164 if (count($tag->getEntries()) === 0) {
165 $em->remove($tag);
166 }
167 }
168
169 $em->flush();
170 }
171}
diff --git a/src/Wallabag/ApiBundle/Controller/WallabagRestController.php b/src/Wallabag/ApiBundle/Controller/WallabagRestController.php
index 50652b77..544c1ea9 100644
--- a/src/Wallabag/ApiBundle/Controller/WallabagRestController.php
+++ b/src/Wallabag/ApiBundle/Controller/WallabagRestController.php
@@ -3,627 +3,11 @@
3namespace Wallabag\ApiBundle\Controller; 3namespace Wallabag\ApiBundle\Controller;
4 4
5use FOS\RestBundle\Controller\FOSRestController; 5use FOS\RestBundle\Controller\FOSRestController;
6use Hateoas\Configuration\Route as HateoasRoute;
7use Hateoas\Representation\Factory\PagerfantaFactory;
8use Nelmio\ApiDocBundle\Annotation\ApiDoc;
9use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
10use Symfony\Component\HttpFoundation\Request;
11use Symfony\Component\HttpFoundation\JsonResponse;
12use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
13use Symfony\Component\Security\Core\Exception\AccessDeniedException; 6use Symfony\Component\Security\Core\Exception\AccessDeniedException;
14use Wallabag\CoreBundle\Entity\Entry; 7use Wallabag\CoreBundle\Entity\Entry;
15use Wallabag\CoreBundle\Entity\Tag;
16use Wallabag\AnnotationBundle\Entity\Annotation;
17use Wallabag\CoreBundle\Event\EntrySavedEvent;
18use Wallabag\CoreBundle\Event\EntryDeletedEvent;
19 8
20class WallabagRestController extends FOSRestController 9class WallabagRestController extends FOSRestController
21{ 10{
22 private function validateAuthentication()
23 {
24 if (false === $this->get('security.authorization_checker')->isGranted('IS_AUTHENTICATED_FULLY')) {
25 throw new AccessDeniedException();
26 }
27 }
28
29 /**
30 * Check if an entry exist by url.
31 *
32 * @ApiDoc(
33 * parameters={
34 * {"name"="url", "dataType"="string", "required"=true, "format"="An url", "description"="Url to check if it exists"},
35 * {"name"="urls", "dataType"="string", "required"=false, "format"="An array of urls (?urls[]=http...&urls[]=http...)", "description"="Urls (as an array) to check if it exists"}
36 * }
37 * )
38 *
39 * @return JsonResponse
40 */
41 public function getEntriesExistsAction(Request $request)
42 {
43 $this->validateAuthentication();
44
45 $urls = $request->query->get('urls', []);
46
47 // handle multiple urls first
48 if (!empty($urls)) {
49 $results = [];
50 foreach ($urls as $url) {
51 $res = $this->getDoctrine()
52 ->getRepository('WallabagCoreBundle:Entry')
53 ->findByUrlAndUserId($url, $this->getUser()->getId());
54
55 $results[$url] = false === $res ? false : true;
56 }
57
58 $json = $this->get('serializer')->serialize($results, 'json');
59
60 return (new JsonResponse())->setJson($json);
61 }
62
63 // let's see if it is a simple url?
64 $url = $request->query->get('url', '');
65
66 if (empty($url)) {
67 throw $this->createAccessDeniedException('URL is empty?, logged user id: '.$this->getUser()->getId());
68 }
69
70 $res = $this->getDoctrine()
71 ->getRepository('WallabagCoreBundle:Entry')
72 ->findByUrlAndUserId($url, $this->getUser()->getId());
73
74 $exists = false === $res ? false : true;
75
76 $json = $this->get('serializer')->serialize(['exists' => $exists], 'json');
77
78 return (new JsonResponse())->setJson($json);
79 }
80
81 /**
82 * Retrieve all entries. It could be filtered by many options.
83 *
84 * @ApiDoc(
85 * parameters={
86 * {"name"="archive", "dataType"="integer", "required"=false, "format"="1 or 0, all entries by default", "description"="filter by archived status."},
87 * {"name"="starred", "dataType"="integer", "required"=false, "format"="1 or 0, all entries by default", "description"="filter by starred status."},
88 * {"name"="sort", "dataType"="string", "required"=false, "format"="'created' or 'updated', default 'created'", "description"="sort entries by date."},
89 * {"name"="order", "dataType"="string", "required"=false, "format"="'asc' or 'desc', default 'desc'", "description"="order of sort."},
90 * {"name"="page", "dataType"="integer", "required"=false, "format"="default '1'", "description"="what page you want."},
91 * {"name"="perPage", "dataType"="integer", "required"=false, "format"="default'30'", "description"="results per page."},
92 * {"name"="tags", "dataType"="string", "required"=false, "format"="api,rest", "description"="a list of tags url encoded. Will returns entries that matches ALL tags."},
93 * {"name"="since", "dataType"="integer", "required"=false, "format"="default '0'", "description"="The timestamp since when you want entries updated."},
94 * }
95 * )
96 *
97 * @return JsonResponse
98 */
99 public function getEntriesAction(Request $request)
100 {
101 $this->validateAuthentication();
102
103 $isArchived = (null === $request->query->get('archive')) ? null : (bool) $request->query->get('archive');
104 $isStarred = (null === $request->query->get('starred')) ? null : (bool) $request->query->get('starred');
105 $sort = $request->query->get('sort', 'created');
106 $order = $request->query->get('order', 'desc');
107 $page = (int) $request->query->get('page', 1);
108 $perPage = (int) $request->query->get('perPage', 30);
109 $tags = $request->query->get('tags', '');
110 $since = $request->query->get('since', 0);
111
112 $pager = $this->getDoctrine()
113 ->getRepository('WallabagCoreBundle:Entry')
114 ->findEntries($this->getUser()->getId(), $isArchived, $isStarred, $sort, $order, $since, $tags);
115
116 $pager->setCurrentPage($page);
117 $pager->setMaxPerPage($perPage);
118
119 $pagerfantaFactory = new PagerfantaFactory('page', 'perPage');
120 $paginatedCollection = $pagerfantaFactory->createRepresentation(
121 $pager,
122 new HateoasRoute(
123 'api_get_entries',
124 [
125 'archive' => $isArchived,
126 'starred' => $isStarred,
127 'sort' => $sort,
128 'order' => $order,
129 'page' => $page,
130 'perPage' => $perPage,
131 'tags' => $tags,
132 'since' => $since,
133 ],
134 UrlGeneratorInterface::ABSOLUTE_URL
135 )
136 );
137
138 $json = $this->get('serializer')->serialize($paginatedCollection, 'json');
139
140 return (new JsonResponse())->setJson($json);
141 }
142
143 /**
144 * Retrieve a single entry.
145 *
146 * @ApiDoc(
147 * requirements={
148 * {"name"="entry", "dataType"="integer", "requirement"="\w+", "description"="The entry ID"}
149 * }
150 * )
151 *
152 * @return JsonResponse
153 */
154 public function getEntryAction(Entry $entry)
155 {
156 $this->validateAuthentication();
157 $this->validateUserAccess($entry->getUser()->getId());
158
159 $json = $this->get('serializer')->serialize($entry, 'json');
160
161 return (new JsonResponse())->setJson($json);
162 }
163
164 /**
165 * Retrieve a single entry as a predefined format.
166 *
167 * @ApiDoc(
168 * requirements={
169 * {"name"="entry", "dataType"="integer", "requirement"="\w+", "description"="The entry ID"}
170 * }
171 * )
172 *
173 * @return Response
174 */
175 public function getEntryExportAction(Entry $entry, Request $request)
176 {
177 $this->validateAuthentication();
178 $this->validateUserAccess($entry->getUser()->getId());
179
180 return $this->get('wallabag_core.helper.entries_export')
181 ->setEntries($entry)
182 ->updateTitle('entry')
183 ->exportAs($request->attributes->get('_format'));
184 }
185
186 /**
187 * Create an entry.
188 *
189 * @ApiDoc(
190 * parameters={
191 * {"name"="url", "dataType"="string", "required"=true, "format"="http://www.test.com/article.html", "description"="Url for the entry."},
192 * {"name"="title", "dataType"="string", "required"=false, "description"="Optional, we'll get the title from the page."},
193 * {"name"="tags", "dataType"="string", "required"=false, "format"="tag1,tag2,tag3", "description"="a comma-separated list of tags."},
194 * {"name"="starred", "dataType"="integer", "required"=false, "format"="1 or 0", "description"="entry already starred"},
195 * {"name"="archive", "dataType"="integer", "required"=false, "format"="1 or 0", "description"="entry already archived"},
196 * }
197 * )
198 *
199 * @return JsonResponse
200 */
201 public function postEntriesAction(Request $request)
202 {
203 $this->validateAuthentication();
204
205 $url = $request->request->get('url');
206 $title = $request->request->get('title');
207 $isArchived = $request->request->get('archive');
208 $isStarred = $request->request->get('starred');
209
210 $entry = $this->get('wallabag_core.entry_repository')->findByUrlAndUserId($url, $this->getUser()->getId());
211
212 if (false === $entry) {
213 $entry = $this->get('wallabag_core.content_proxy')->updateEntry(
214 new Entry($this->getUser()),
215 $url
216 );
217 }
218
219 if (!is_null($title)) {
220 $entry->setTitle($title);
221 }
222
223 $tags = $request->request->get('tags', '');
224 if (!empty($tags)) {
225 $this->get('wallabag_core.content_proxy')->assignTagsToEntry($entry, $tags);
226 }
227
228 if (!is_null($isStarred)) {
229 $entry->setStarred((bool) $isStarred);
230 }
231
232 if (!is_null($isArchived)) {
233 $entry->setArchived((bool) $isArchived);
234 }
235
236 $em = $this->getDoctrine()->getManager();
237 $em->persist($entry);
238 $em->flush();
239
240 // entry saved, dispatch event about it!
241 $this->get('event_dispatcher')->dispatch(EntrySavedEvent::NAME, new EntrySavedEvent($entry));
242
243 $json = $this->get('serializer')->serialize($entry, 'json');
244
245 return (new JsonResponse())->setJson($json);
246 }
247
248 /**
249 * Change several properties of an entry.
250 *
251 * @ApiDoc(
252 * requirements={
253 * {"name"="entry", "dataType"="integer", "requirement"="\w+", "description"="The entry ID"}
254 * },
255 * parameters={
256 * {"name"="title", "dataType"="string", "required"=false},
257 * {"name"="tags", "dataType"="string", "required"=false, "format"="tag1,tag2,tag3", "description"="a comma-separated list of tags."},
258 * {"name"="archive", "dataType"="integer", "required"=false, "format"="1 or 0", "description"="archived the entry."},
259 * {"name"="starred", "dataType"="integer", "required"=false, "format"="1 or 0", "description"="starred the entry."},
260 * }
261 * )
262 *
263 * @return JsonResponse
264 */
265 public function patchEntriesAction(Entry $entry, Request $request)
266 {
267 $this->validateAuthentication();
268 $this->validateUserAccess($entry->getUser()->getId());
269
270 $title = $request->request->get('title');
271 $isArchived = $request->request->get('archive');
272 $isStarred = $request->request->get('starred');
273
274 if (!is_null($title)) {
275 $entry->setTitle($title);
276 }
277
278 if (!is_null($isArchived)) {
279 $entry->setArchived((bool) $isArchived);
280 }
281
282 if (!is_null($isStarred)) {
283 $entry->setStarred((bool) $isStarred);
284 }
285
286 $tags = $request->request->get('tags', '');
287 if (!empty($tags)) {
288 $this->get('wallabag_core.content_proxy')->assignTagsToEntry($entry, $tags);
289 }
290
291 $em = $this->getDoctrine()->getManager();
292 $em->flush();
293
294 $json = $this->get('serializer')->serialize($entry, 'json');
295
296 return (new JsonResponse())->setJson($json);
297 }
298
299 /**
300 * Delete **permanently** an entry.
301 *
302 * @ApiDoc(
303 * requirements={
304 * {"name"="entry", "dataType"="integer", "requirement"="\w+", "description"="The entry ID"}
305 * }
306 * )
307 *
308 * @return JsonResponse
309 */
310 public function deleteEntriesAction(Entry $entry)
311 {
312 $this->validateAuthentication();
313 $this->validateUserAccess($entry->getUser()->getId());
314
315 // entry deleted, dispatch event about it!
316 $this->get('event_dispatcher')->dispatch(EntryDeletedEvent::NAME, new EntryDeletedEvent($entry));
317
318 $em = $this->getDoctrine()->getManager();
319 $em->remove($entry);
320 $em->flush();
321
322 $json = $this->get('serializer')->serialize($entry, 'json');
323
324 return (new JsonResponse())->setJson($json);
325 }
326
327 /**
328 * Retrieve all tags for an entry.
329 *
330 * @ApiDoc(
331 * requirements={
332 * {"name"="entry", "dataType"="integer", "requirement"="\w+", "description"="The entry ID"}
333 * }
334 * )
335 *
336 * @return JsonResponse
337 */
338 public function getEntriesTagsAction(Entry $entry)
339 {
340 $this->validateAuthentication();
341 $this->validateUserAccess($entry->getUser()->getId());
342
343 $json = $this->get('serializer')->serialize($entry->getTags(), 'json');
344
345 return (new JsonResponse())->setJson($json);
346 }
347
348 /**
349 * Add one or more tags to an entry.
350 *
351 * @ApiDoc(
352 * requirements={
353 * {"name"="entry", "dataType"="integer", "requirement"="\w+", "description"="The entry ID"}
354 * },
355 * parameters={
356 * {"name"="tags", "dataType"="string", "required"=false, "format"="tag1,tag2,tag3", "description"="a comma-separated list of tags."},
357 * }
358 * )
359 *
360 * @return JsonResponse
361 */
362 public function postEntriesTagsAction(Request $request, Entry $entry)
363 {
364 $this->validateAuthentication();
365 $this->validateUserAccess($entry->getUser()->getId());
366
367 $tags = $request->request->get('tags', '');
368 if (!empty($tags)) {
369 $this->get('wallabag_core.content_proxy')->assignTagsToEntry($entry, $tags);
370 }
371
372 $em = $this->getDoctrine()->getManager();
373 $em->persist($entry);
374 $em->flush();
375
376 $json = $this->get('serializer')->serialize($entry, 'json');
377
378 return (new JsonResponse())->setJson($json);
379 }
380
381 /**
382 * Permanently remove one tag for an entry.
383 *
384 * @ApiDoc(
385 * requirements={
386 * {"name"="tag", "dataType"="integer", "requirement"="\w+", "description"="The tag ID"},
387 * {"name"="entry", "dataType"="integer", "requirement"="\w+", "description"="The entry ID"}
388 * }
389 * )
390 *
391 * @return JsonResponse
392 */
393 public function deleteEntriesTagsAction(Entry $entry, Tag $tag)
394 {
395 $this->validateAuthentication();
396 $this->validateUserAccess($entry->getUser()->getId());
397
398 $entry->removeTag($tag);
399 $em = $this->getDoctrine()->getManager();
400 $em->persist($entry);
401 $em->flush();
402
403 $json = $this->get('serializer')->serialize($entry, 'json');
404
405 return (new JsonResponse())->setJson($json);
406 }
407
408 /**
409 * Retrieve all tags.
410 *
411 * @ApiDoc()
412 *
413 * @return JsonResponse
414 */
415 public function getTagsAction()
416 {
417 $this->validateAuthentication();
418
419 $tags = $this->getDoctrine()
420 ->getRepository('WallabagCoreBundle:Tag')
421 ->findAllTags($this->getUser()->getId());
422
423 $json = $this->get('serializer')->serialize($tags, 'json');
424
425 return (new JsonResponse())->setJson($json);
426 }
427
428 /**
429 * Permanently remove one tag from **every** entry.
430 *
431 * @ApiDoc(
432 * requirements={
433 * {"name"="tag", "dataType"="string", "required"=true, "requirement"="\w+", "description"="Tag as a string"}
434 * }
435 * )
436 *
437 * @return JsonResponse
438 */
439 public function deleteTagLabelAction(Request $request)
440 {
441 $this->validateAuthentication();
442 $label = $request->request->get('tag', '');
443
444 $tag = $this->getDoctrine()->getRepository('WallabagCoreBundle:Tag')->findOneByLabel($label);
445
446 if (empty($tag)) {
447 throw $this->createNotFoundException('Tag not found');
448 }
449
450 $this->getDoctrine()
451 ->getRepository('WallabagCoreBundle:Entry')
452 ->removeTag($this->getUser()->getId(), $tag);
453
454 $this->cleanOrphanTag($tag);
455
456 $json = $this->get('serializer')->serialize($tag, 'json');
457
458 return (new JsonResponse())->setJson($json);
459 }
460
461 /**
462 * Permanently remove some tags from **every** entry.
463 *
464 * @ApiDoc(
465 * requirements={
466 * {"name"="tags", "dataType"="string", "required"=true, "format"="tag1,tag2", "description"="Tags as strings (comma splitted)"}
467 * }
468 * )
469 *
470 * @return JsonResponse
471 */
472 public function deleteTagsLabelAction(Request $request)
473 {
474 $this->validateAuthentication();
475
476 $tagsLabels = $request->request->get('tags', '');
477
478 $tags = [];
479
480 foreach (explode(',', $tagsLabels) as $tagLabel) {
481 $tagEntity = $this->getDoctrine()->getRepository('WallabagCoreBundle:Tag')->findOneByLabel($tagLabel);
482
483 if (!empty($tagEntity)) {
484 $tags[] = $tagEntity;
485 }
486 }
487
488 if (empty($tags)) {
489 throw $this->createNotFoundException('Tags not found');
490 }
491
492 $this->getDoctrine()
493 ->getRepository('WallabagCoreBundle:Entry')
494 ->removeTags($this->getUser()->getId(), $tags);
495
496 $this->cleanOrphanTag($tags);
497
498 $json = $this->get('serializer')->serialize($tags, 'json');
499
500 return (new JsonResponse())->setJson($json);
501 }
502
503 /**
504 * Permanently remove one tag from **every** entry.
505 *
506 * @ApiDoc(
507 * requirements={
508 * {"name"="tag", "dataType"="integer", "requirement"="\w+", "description"="The tag"}
509 * }
510 * )
511 *
512 * @return JsonResponse
513 */
514 public function deleteTagAction(Tag $tag)
515 {
516 $this->validateAuthentication();
517
518 $this->getDoctrine()
519 ->getRepository('WallabagCoreBundle:Entry')
520 ->removeTag($this->getUser()->getId(), $tag);
521
522 $this->cleanOrphanTag($tag);
523
524 $json = $this->get('serializer')->serialize($tag, 'json');
525
526 return (new JsonResponse())->setJson($json);
527 }
528
529 /**
530 * Retrieve annotations for an entry.
531 *
532 * @ApiDoc(
533 * requirements={
534 * {"name"="entry", "dataType"="integer", "requirement"="\w+", "description"="The entry ID"}
535 * }
536 * )
537 *
538 * @param Entry $entry
539 *
540 * @return JsonResponse
541 */
542 public function getAnnotationsAction(Entry $entry)
543 {
544 $this->validateAuthentication();
545
546 return $this->forward('WallabagAnnotationBundle:WallabagAnnotation:getAnnotations', [
547 'entry' => $entry,
548 ]);
549 }
550
551 /**
552 * Creates a new annotation.
553 *
554 * @ApiDoc(
555 * requirements={
556 * {"name"="ranges", "dataType"="array", "requirement"="\w+", "description"="The range array for the annotation"},
557 * {"name"="quote", "dataType"="string", "required"=false, "description"="Optional, quote for the annotation"},
558 * {"name"="text", "dataType"="string", "required"=true, "description"=""},
559 * }
560 * )
561 *
562 * @param Request $request
563 * @param Entry $entry
564 *
565 * @return JsonResponse
566 */
567 public function postAnnotationAction(Request $request, Entry $entry)
568 {
569 $this->validateAuthentication();
570
571 return $this->forward('WallabagAnnotationBundle:WallabagAnnotation:postAnnotation', [
572 'request' => $request,
573 'entry' => $entry,
574 ]);
575 }
576
577 /**
578 * Updates an annotation.
579 *
580 * @ApiDoc(
581 * requirements={
582 * {"name"="annotation", "dataType"="string", "requirement"="\w+", "description"="The annotation ID"}
583 * }
584 * )
585 *
586 * @ParamConverter("annotation", class="WallabagAnnotationBundle:Annotation")
587 *
588 * @param Annotation $annotation
589 * @param Request $request
590 *
591 * @return JsonResponse
592 */
593 public function putAnnotationAction(Annotation $annotation, Request $request)
594 {
595 $this->validateAuthentication();
596
597 return $this->forward('WallabagAnnotationBundle:WallabagAnnotation:putAnnotation', [
598 'annotation' => $annotation,
599 'request' => $request,
600 ]);
601 }
602
603 /**
604 * Removes an annotation.
605 *
606 * @ApiDoc(
607 * requirements={
608 * {"name"="annotation", "dataType"="string", "requirement"="\w+", "description"="The annotation ID"}
609 * }
610 * )
611 *
612 * @ParamConverter("annotation", class="WallabagAnnotationBundle:Annotation")
613 *
614 * @param Annotation $annotation
615 *
616 * @return JsonResponse
617 */
618 public function deleteAnnotationAction(Annotation $annotation)
619 {
620 $this->validateAuthentication();
621
622 return $this->forward('WallabagAnnotationBundle:WallabagAnnotation:deleteAnnotation', [
623 'annotation' => $annotation,
624 ]);
625 }
626
627 /** 11 /**
628 * Retrieve version number. 12 * Retrieve version number.
629 * 13 *
@@ -634,32 +18,15 @@ class WallabagRestController extends FOSRestController
634 public function getVersionAction() 18 public function getVersionAction()
635 { 19 {
636 $version = $this->container->getParameter('wallabag_core.version'); 20 $version = $this->container->getParameter('wallabag_core.version');
637
638 $json = $this->get('serializer')->serialize($version, 'json'); 21 $json = $this->get('serializer')->serialize($version, 'json');
639
640 return (new JsonResponse())->setJson($json); 22 return (new JsonResponse())->setJson($json);
641 } 23 }
642 24
643 /** 25 protected function validateAuthentication()
644 * Remove orphan tag in case no entries are associated to it.
645 *
646 * @param Tag|array $tags
647 */
648 private function cleanOrphanTag($tags)
649 { 26 {
650 if (!is_array($tags)) { 27 if (false === $this->get('security.authorization_checker')->isGranted('IS_AUTHENTICATED_FULLY')) {
651 $tags = [$tags]; 28 throw new AccessDeniedException();
652 }
653
654 $em = $this->getDoctrine()->getManager();
655
656 foreach ($tags as $tag) {
657 if (count($tag->getEntries()) === 0) {
658 $em->remove($tag);
659 }
660 } 29 }
661
662 $em->flush();
663 } 30 }
664 31
665 /** 32 /**
@@ -668,7 +35,7 @@ class WallabagRestController extends FOSRestController
668 * 35 *
669 * @param int $requestUserId User id from the requested source 36 * @param int $requestUserId User id from the requested source
670 */ 37 */
671 private function validateUserAccess($requestUserId) 38 protected function validateUserAccess($requestUserId)
672 { 39 {
673 $user = $this->get('security.token_storage')->getToken()->getUser(); 40 $user = $this->get('security.token_storage')->getToken()->getUser();
674 if ($requestUserId != $user->getId()) { 41 if ($requestUserId != $user->getId()) {
diff --git a/src/Wallabag/ApiBundle/Resources/config/routing_rest.yml b/src/Wallabag/ApiBundle/Resources/config/routing_rest.yml
index 35f8b2c1..8e1886ac 100644
--- a/src/Wallabag/ApiBundle/Resources/config/routing_rest.yml
+++ b/src/Wallabag/ApiBundle/Resources/config/routing_rest.yml
@@ -1,4 +1,14 @@
1api: 1entry:
2 type: rest 2 type: rest
3 resource: "WallabagApiBundle:WallabagRest" 3 resource: "WallabagApiBundle:EntryRest"
4 name_prefix: api_ 4 name_prefix: api_
5
6tag:
7 type: rest
8 resource: "WallabagApiBundle:TagRest"
9 name_prefix: api_
10
11misc:
12 type: rest
13 resource: "WallabagApiBundle:WallabagRest"
14 name_prefix: api_