]> git.immae.eu Git - github/wallabag/wallabag.git/blame - src/Wallabag/ApiBundle/Controller/WallabagRestController.php
ApiDoc & Route annotation were conflicted
[github/wallabag/wallabag.git] / src / Wallabag / ApiBundle / Controller / WallabagRestController.php
CommitLineData
f8bf8952
NL
1<?php
2
769e19dc 3namespace Wallabag\ApiBundle\Controller;
f8bf8952 4
fcb1fba5 5use FOS\RestBundle\Controller\FOSRestController;
3f3a6087 6use Hateoas\Configuration\Route as HateoasRoute;
619cc453 7use Hateoas\Representation\Factory\PagerfantaFactory;
f8bf8952 8use Nelmio\ApiDocBundle\Annotation\ApiDoc;
27f15aa4 9use Symfony\Component\HttpFoundation\Request;
60faee00 10use Symfony\Component\HttpFoundation\JsonResponse;
1d76102a 11use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
b0b893ea 12use Symfony\Component\Security\Core\Exception\AccessDeniedException;
be463487 13use Wallabag\CoreBundle\Entity\Entry;
653e8be4 14use Wallabag\CoreBundle\Entity\Tag;
3f3a6087 15use FOS\RestBundle\Controller\Annotations\Route;
f8bf8952 16
fcb1fba5 17class WallabagRestController extends FOSRestController
f8bf8952 18{
77273253
NL
19 private function validateAuthentication()
20 {
18f8f32f 21 if (false === $this->get('security.authorization_checker')->isGranted('IS_AUTHENTICATED_FULLY')) {
77273253
NL
22 throw new AccessDeniedException();
23 }
24 }
25
6273fefd 26 /**
3583cadf 27 * Check if an entry exist by url.
6273fefd
JB
28 *
29 * @ApiDoc(
30 * parameters={
f0abc22d
JB
31 * {"name"="url", "dataType"="string", "required"=true, "format"="An url", "description"="Url to check if it exists"},
32 * {"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"}
6273fefd
JB
33 * }
34 * )
35 *
36 * @return JsonResponse
37 */
38 public function getEntriesExistsAction(Request $request)
39 {
40 $this->validateAuthentication();
41
f0abc22d
JB
42 $urls = $request->query->get('urls', []);
43
44 // handle multiple urls first
45 if (!empty($urls)) {
46 $results = [];
47 foreach ($urls as $url) {
48 $res = $this->getDoctrine()
49 ->getRepository('WallabagCoreBundle:Entry')
50 ->findByUrlAndUserId($url, $this->getUser()->getId());
51
52 $results[$url] = false === $res ? false : true;
53 }
54
55 $json = $this->get('serializer')->serialize($results, 'json');
56
57 return (new JsonResponse())->setJson($json);
58 }
59
60 // let's see if it is a simple url?
6273fefd
JB
61 $url = $request->query->get('url', '');
62
63 if (empty($url)) {
0b174d69 64 throw $this->createAccessDeniedException('URL is empty?, logged user id: '.$this->getUser()->getId());
6273fefd
JB
65 }
66
67 $res = $this->getDoctrine()
68 ->getRepository('WallabagCoreBundle:Entry')
69 ->findByUrlAndUserId($url, $this->getUser()->getId());
70
71 $exists = false === $res ? false : true;
72
73 $json = $this->get('serializer')->serialize(['exists' => $exists], 'json');
74
75 return (new JsonResponse())->setJson($json);
76 }
77
f8bf8952 78 /**
a8c90c5c 79 * Retrieve all entries. It could be filtered by many options.
f8bf8952
NL
80 *
81 * @ApiDoc(
a8c90c5c 82 * parameters={
189ef634
TC
83 * {"name"="archive", "dataType"="integer", "required"=false, "format"="1 or 0, all entries by default", "description"="filter by archived status."},
84 * {"name"="starred", "dataType"="integer", "required"=false, "format"="1 or 0, all entries by default", "description"="filter by starred status."},
a8c90c5c
NL
85 * {"name"="sort", "dataType"="string", "required"=false, "format"="'created' or 'updated', default 'created'", "description"="sort entries by date."},
86 * {"name"="order", "dataType"="string", "required"=false, "format"="'asc' or 'desc', default 'desc'", "description"="order of sort."},
87 * {"name"="page", "dataType"="integer", "required"=false, "format"="default '1'", "description"="what page you want."},
88 * {"name"="perPage", "dataType"="integer", "required"=false, "format"="default'30'", "description"="results per page."},
189ef634 89 * {"name"="tags", "dataType"="string", "required"=false, "format"="api,rest", "description"="a list of tags url encoded. Will returns entries that matches ALL tags."},
e5fb89e5 90 * {"name"="since", "dataType"="integer", "required"=false, "format"="default '0'", "description"="The timestamp since when you want entries updated."},
a8c90c5c 91 * }
f8bf8952 92 * )
4346a860 93 *
60faee00 94 * @return JsonResponse
f8bf8952 95 */
27f15aa4 96 public function getEntriesAction(Request $request)
f8bf8952 97 {
77273253
NL
98 $this->validateAuthentication();
99
0135c98b
NL
100 $isArchived = (null === $request->query->get('archive')) ? null : (bool) $request->query->get('archive');
101 $isStarred = (null === $request->query->get('starred')) ? null : (bool) $request->query->get('starred');
8ce32af6
JB
102 $sort = $request->query->get('sort', 'created');
103 $order = $request->query->get('order', 'desc');
104 $page = (int) $request->query->get('page', 1);
105 $perPage = (int) $request->query->get('perPage', 30);
28803f10 106 $tags = $request->query->get('tags', '');
c3f8b428 107 $since = $request->query->get('since', 0);
a8c90c5c 108
fc732227 109 $pager = $this->getDoctrine()
be463487 110 ->getRepository('WallabagCoreBundle:Entry')
28803f10 111 ->findEntries($this->getUser()->getId(), $isArchived, $isStarred, $sort, $order, $since, $tags);
a8c90c5c 112
6e22bd73
WD
113 $pager->setCurrentPage($page);
114 $pager->setMaxPerPage($perPage);
115
8ce32af6 116 $pagerfantaFactory = new PagerfantaFactory('page', 'perPage');
6e22bd73
WD
117 $paginatedCollection = $pagerfantaFactory->createRepresentation(
118 $pager,
3f3a6087 119 new HateoasRoute(
c3f8b428
JB
120 'api_get_entries',
121 [
122 'archive' => $isArchived,
123 'starred' => $isStarred,
124 'sort' => $sort,
125 'order' => $order,
126 'page' => $page,
127 'perPage' => $perPage,
128 'tags' => $tags,
129 'since' => $since,
130 ],
131 UrlGeneratorInterface::ABSOLUTE_URL
132 )
6e22bd73
WD
133 );
134
135 $json = $this->get('serializer')->serialize($paginatedCollection, 'json');
0f006880 136
60faee00 137 return (new JsonResponse())->setJson($json);
f8bf8952
NL
138 }
139
140 /**
4346a860 141 * Retrieve a single entry.
f8bf8952
NL
142 *
143 * @ApiDoc(
144 * requirements={
145 * {"name"="entry", "dataType"="integer", "requirement"="\w+", "description"="The entry ID"}
146 * }
147 * )
4346a860 148 *
60faee00 149 * @return JsonResponse
f8bf8952 150 */
be463487 151 public function getEntryAction(Entry $entry)
f8bf8952 152 {
77273253 153 $this->validateAuthentication();
fcb1fba5 154 $this->validateUserAccess($entry->getUser()->getId());
092ca707 155
aa4d6562 156 $json = $this->get('serializer')->serialize($entry, 'json');
0f006880 157
60faee00 158 return (new JsonResponse())->setJson($json);
f8bf8952
NL
159 }
160
3f3a6087
JB
161 /**
162 * Retrieve a single entry as a predefined format.
163 *
164 * @ApiDoc(
165 * requirements={
166 * {"name"="entry", "dataType"="integer", "requirement"="\w+", "description"="The entry ID"}
167 * }
168 * )
169 *
3f3a6087
JB
170 * @return Response
171 */
172 public function getEntryExportAction(Entry $entry, Request $request)
173 {
174 $this->validateAuthentication();
175 $this->validateUserAccess($entry->getUser()->getId());
176
177 return $this->get('wallabag_core.helper.entries_export')
178 ->setEntries($entry)
179 ->updateTitle('entry')
180 ->exportAs($request->attributes->get('_format'));
181 }
182
f8bf8952 183 /**
4346a860 184 * Create an entry.
f8bf8952
NL
185 *
186 * @ApiDoc(
a8c90c5c
NL
187 * parameters={
188 * {"name"="url", "dataType"="string", "required"=true, "format"="http://www.test.com/article.html", "description"="Url for the entry."},
189 * {"name"="title", "dataType"="string", "required"=false, "description"="Optional, we'll get the title from the page."},
190 * {"name"="tags", "dataType"="string", "required"=false, "format"="tag1,tag2,tag3", "description"="a comma-separated list of tags."},
189ef634
TC
191 * {"name"="starred", "dataType"="integer", "required"=false, "format"="1 or 0", "description"="entry already starred"},
192 * {"name"="archive", "dataType"="integer", "required"=false, "format"="1 or 0", "description"="entry already archived"},
a8c90c5c 193 * }
f8bf8952 194 * )
4346a860 195 *
60faee00 196 * @return JsonResponse
f8bf8952 197 */
843dbe51 198 public function postEntriesAction(Request $request)
f8bf8952 199 {
77273253
NL
200 $this->validateAuthentication();
201
c3235553 202 $url = $request->request->get('url');
51a15609 203 $title = $request->request->get('title');
873e3806
YE
204 $isArchived = $request->request->get('archive');
205 $isStarred = $request->request->get('starred');
c3235553 206
3107f92a
TC
207 $entry = $this->get('wallabag_core.entry_repository')->findByUrlAndUserId($url, $this->getUser()->getId());
208
209 if (false === $entry) {
210 $entry = $this->get('wallabag_core.content_proxy')->updateEntry(
211 new Entry($this->getUser()),
212 $url
213 );
214 }
092ca707 215
51a15609
NL
216 if (!is_null($title)) {
217 $entry->setTitle($title);
218 }
219
0ca374e6
NL
220 $tags = $request->request->get('tags', '');
221 if (!empty($tags)) {
c2656f96 222 $this->get('wallabag_core.content_proxy')->assignTagsToEntry($entry, $tags);
0ca374e6 223 }
092ca707 224
bc2b947c
TC
225 if (!is_null($isStarred)) {
226 $entry->setStarred((bool) $isStarred);
227 }
816ad405 228
bc2b947c
TC
229 if (!is_null($isArchived)) {
230 $entry->setArchived((bool) $isArchived);
231 }
816ad405 232
843dbe51
NL
233 $em = $this->getDoctrine()->getManager();
234 $em->persist($entry);
816ad405 235
843dbe51
NL
236 $em->flush();
237
aa4d6562
NL
238 $json = $this->get('serializer')->serialize($entry, 'json');
239
60faee00 240 return (new JsonResponse())->setJson($json);
f8bf8952
NL
241 }
242
243 /**
4346a860 244 * Change several properties of an entry.
f8bf8952
NL
245 *
246 * @ApiDoc(
247 * requirements={
248 * {"name"="entry", "dataType"="integer", "requirement"="\w+", "description"="The entry ID"}
a8c90c5c
NL
249 * },
250 * parameters={
251 * {"name"="title", "dataType"="string", "required"=false},
252 * {"name"="tags", "dataType"="string", "required"=false, "format"="tag1,tag2,tag3", "description"="a comma-separated list of tags."},
189ef634
TC
253 * {"name"="archive", "dataType"="integer", "required"=false, "format"="1 or 0", "description"="archived the entry."},
254 * {"name"="starred", "dataType"="integer", "required"=false, "format"="1 or 0", "description"="starred the entry."},
1d147791 255 * }
f8bf8952 256 * )
4346a860 257 *
60faee00 258 * @return JsonResponse
f8bf8952 259 */
be463487 260 public function patchEntriesAction(Entry $entry, Request $request)
f8bf8952 261 {
77273253 262 $this->validateAuthentication();
fcb1fba5 263 $this->validateUserAccess($entry->getUser()->getId());
092ca707 264
8ce32af6 265 $title = $request->request->get('title');
614a0bfd
YE
266 $isArchived = $request->request->get('archive');
267 $isStarred = $request->request->get('starred');
2c093b03
NL
268
269 if (!is_null($title)) {
270 $entry->setTitle($title);
271 }
272
273 if (!is_null($isArchived)) {
189ef634 274 $entry->setArchived((bool) $isArchived);
2c093b03
NL
275 }
276
2c093b03 277 if (!is_null($isStarred)) {
189ef634 278 $entry->setStarred((bool) $isStarred);
2c093b03
NL
279 }
280
0ca374e6
NL
281 $tags = $request->request->get('tags', '');
282 if (!empty($tags)) {
c2656f96 283 $this->get('wallabag_core.content_proxy')->assignTagsToEntry($entry, $tags);
0ca374e6 284 }
092ca707 285
2c093b03 286 $em = $this->getDoctrine()->getManager();
2c093b03
NL
287 $em->flush();
288
0ca374e6
NL
289 $json = $this->get('serializer')->serialize($entry, 'json');
290
60faee00 291 return (new JsonResponse())->setJson($json);
f8bf8952
NL
292 }
293
294 /**
4346a860 295 * Delete **permanently** an entry.
f8bf8952
NL
296 *
297 * @ApiDoc(
a8c90c5c
NL
298 * requirements={
299 * {"name"="entry", "dataType"="integer", "requirement"="\w+", "description"="The entry ID"}
300 * }
f8bf8952 301 * )
4346a860 302 *
60faee00 303 * @return JsonResponse
f8bf8952 304 */
be463487 305 public function deleteEntriesAction(Entry $entry)
f8bf8952 306 {
77273253 307 $this->validateAuthentication();
fcb1fba5 308 $this->validateUserAccess($entry->getUser()->getId());
092ca707 309
42a90646 310 $em = $this->getDoctrine()->getManager();
1d147791 311 $em->remove($entry);
42a90646
NL
312 $em->flush();
313
1d147791
NL
314 $json = $this->get('serializer')->serialize($entry, 'json');
315
60faee00 316 return (new JsonResponse())->setJson($json);
f8bf8952
NL
317 }
318
319 /**
4346a860 320 * Retrieve all tags for an entry.
f8bf8952
NL
321 *
322 * @ApiDoc(
323 * requirements={
324 * {"name"="entry", "dataType"="integer", "requirement"="\w+", "description"="The entry ID"}
325 * }
326 * )
8eedc8cf 327 *
60faee00 328 * @return JsonResponse
f8bf8952 329 */
be463487 330 public function getEntriesTagsAction(Entry $entry)
7df80cb3 331 {
77273253 332 $this->validateAuthentication();
fcb1fba5 333 $this->validateUserAccess($entry->getUser()->getId());
092ca707 334
1bd12b62 335 $json = $this->get('serializer')->serialize($entry->getTags(), 'json');
0a018fe0 336
60faee00 337 return (new JsonResponse())->setJson($json);
f8bf8952
NL
338 }
339
340 /**
4346a860 341 * Add one or more tags to an entry.
f8bf8952
NL
342 *
343 * @ApiDoc(
344 * requirements={
345 * {"name"="entry", "dataType"="integer", "requirement"="\w+", "description"="The entry ID"}
a8c90c5c
NL
346 * },
347 * parameters={
348 * {"name"="tags", "dataType"="string", "required"=false, "format"="tag1,tag2,tag3", "description"="a comma-separated list of tags."},
349 * }
f8bf8952 350 * )
8eedc8cf 351 *
60faee00 352 * @return JsonResponse
f8bf8952 353 */
a36737f4 354 public function postEntriesTagsAction(Request $request, Entry $entry)
7df80cb3 355 {
77273253 356 $this->validateAuthentication();
fcb1fba5 357 $this->validateUserAccess($entry->getUser()->getId());
a36737f4 358
0ca374e6
NL
359 $tags = $request->request->get('tags', '');
360 if (!empty($tags)) {
c2656f96 361 $this->get('wallabag_core.content_proxy')->assignTagsToEntry($entry, $tags);
0ca374e6 362 }
092ca707 363
a36737f4
NL
364 $em = $this->getDoctrine()->getManager();
365 $em->persist($entry);
366 $em->flush();
367
368 $json = $this->get('serializer')->serialize($entry, 'json');
369
60faee00 370 return (new JsonResponse())->setJson($json);
f8bf8952
NL
371 }
372
373 /**
4346a860 374 * Permanently remove one tag for an entry.
f8bf8952
NL
375 *
376 * @ApiDoc(
377 * requirements={
769e19dc 378 * {"name"="tag", "dataType"="integer", "requirement"="\w+", "description"="The tag ID"},
f8bf8952
NL
379 * {"name"="entry", "dataType"="integer", "requirement"="\w+", "description"="The entry ID"}
380 * }
381 * )
8eedc8cf 382 *
60faee00 383 * @return JsonResponse
f8bf8952 384 */
653e8be4 385 public function deleteEntriesTagsAction(Entry $entry, Tag $tag)
f8bf8952 386 {
77273253 387 $this->validateAuthentication();
fcb1fba5 388 $this->validateUserAccess($entry->getUser()->getId());
092ca707
NL
389
390 $entry->removeTag($tag);
391 $em = $this->getDoctrine()->getManager();
392 $em->persist($entry);
393 $em->flush();
394
395 $json = $this->get('serializer')->serialize($entry, 'json');
396
60faee00 397 return (new JsonResponse())->setJson($json);
f8bf8952
NL
398 }
399
400 /**
4346a860 401 * Retrieve all tags.
f8bf8952 402 *
092ca707 403 * @ApiDoc()
8eedc8cf 404 *
60faee00 405 * @return JsonResponse
f8bf8952 406 */
092ca707 407 public function getTagsAction()
7df80cb3 408 {
77273253 409 $this->validateAuthentication();
fc732227
JB
410
411 $tags = $this->getDoctrine()
412 ->getRepository('WallabagCoreBundle:Tag')
28bb4890 413 ->findAllTags($this->getUser()->getId());
fc732227
JB
414
415 $json = $this->get('serializer')->serialize($tags, 'json');
092ca707 416
60faee00 417 return (new JsonResponse())->setJson($json);
f8bf8952
NL
418 }
419
f8bf8952 420 /**
4346a860 421 * Permanently remove one tag from **every** entry.
f8bf8952
NL
422 *
423 * @ApiDoc(
424 * requirements={
a0e1eafc 425 * {"name"="tag", "dataType"="string", "required"=true, "requirement"="\w+", "description"="Tag as a string"}
f8bf8952
NL
426 * }
427 * )
8eedc8cf 428 *
60faee00 429 * @return JsonResponse
f8bf8952 430 */
a0e1eafc 431 public function deleteTagLabelAction(Request $request)
f8bf8952 432 {
77273253 433 $this->validateAuthentication();
a0e1eafc
JB
434 $label = $request->request->get('tag', '');
435
436 $tag = $this->getDoctrine()->getRepository('WallabagCoreBundle:Tag')->findOneByLabel($label);
437
438 if (empty($tag)) {
439 throw $this->createNotFoundException('Tag not found');
440 }
fc732227
JB
441
442 $this->getDoctrine()
443 ->getRepository('WallabagCoreBundle:Entry')
444 ->removeTag($this->getUser()->getId(), $tag);
092ca707 445
ac8cf632
JB
446 $this->cleanOrphanTag($tag);
447
092ca707
NL
448 $json = $this->get('serializer')->serialize($tag, 'json');
449
60faee00 450 return (new JsonResponse())->setJson($json);
769e19dc 451 }
4da01f49
TC
452
453 /**
a0e1eafc 454 * Permanently remove some tags from **every** entry.
4da01f49
TC
455 *
456 * @ApiDoc(
457 * requirements={
a0e1eafc 458 * {"name"="tags", "dataType"="string", "required"=true, "format"="tag1,tag2", "description"="Tags as strings (comma splitted)"}
4da01f49
TC
459 * }
460 * )
461 *
60faee00 462 * @return JsonResponse
4da01f49 463 */
a0e1eafc 464 public function deleteTagsLabelAction(Request $request)
4da01f49
TC
465 {
466 $this->validateAuthentication();
4da01f49 467
a0e1eafc
JB
468 $tagsLabels = $request->request->get('tags', '');
469
470 $tags = [];
471
472 foreach (explode(',', $tagsLabels) as $tagLabel) {
473 $tagEntity = $this->getDoctrine()->getRepository('WallabagCoreBundle:Tag')->findOneByLabel($tagLabel);
474
475 if (!empty($tagEntity)) {
476 $tags[] = $tagEntity;
477 }
478 }
479
480 if (empty($tags)) {
481 throw $this->createNotFoundException('Tags not found');
482 }
483
4da01f49
TC
484 $this->getDoctrine()
485 ->getRepository('WallabagCoreBundle:Entry')
a0e1eafc 486 ->removeTags($this->getUser()->getId(), $tags);
4da01f49 487
ac8cf632
JB
488 $this->cleanOrphanTag($tags);
489
a0e1eafc 490 $json = $this->get('serializer')->serialize($tags, 'json');
4da01f49 491
60faee00 492 return (new JsonResponse())->setJson($json);
4da01f49
TC
493 }
494
495 /**
a0e1eafc 496 * Permanently remove one tag from **every** entry.
4da01f49
TC
497 *
498 * @ApiDoc(
499 * requirements={
a0e1eafc 500 * {"name"="tag", "dataType"="integer", "requirement"="\w+", "description"="The tag"}
4da01f49
TC
501 * }
502 * )
503 *
60faee00 504 * @return JsonResponse
4da01f49 505 */
a0e1eafc 506 public function deleteTagAction(Tag $tag)
4da01f49
TC
507 {
508 $this->validateAuthentication();
509
4da01f49
TC
510 $this->getDoctrine()
511 ->getRepository('WallabagCoreBundle:Entry')
a0e1eafc 512 ->removeTag($this->getUser()->getId(), $tag);
4da01f49 513
ac8cf632
JB
514 $this->cleanOrphanTag($tag);
515
a0e1eafc 516 $json = $this->get('serializer')->serialize($tag, 'json');
4da01f49 517
60faee00 518 return (new JsonResponse())->setJson($json);
4da01f49
TC
519 }
520
2b477030 521 /**
6f8310b4
TC
522 * Retrieve version number.
523 *
524 * @ApiDoc()
2b477030 525 *
60faee00 526 * @return JsonResponse
2b477030
V
527 */
528 public function getVersionAction()
529 {
530 $version = $this->container->getParameter('wallabag_core.version');
531
532 $json = $this->get('serializer')->serialize($version, 'json');
533
60faee00 534 return (new JsonResponse())->setJson($json);
2b477030 535 }
769e19dc 536
ac8cf632
JB
537 /**
538 * Remove orphan tag in case no entries are associated to it.
539 *
540 * @param Tag|array $tags
541 */
542 private function cleanOrphanTag($tags)
543 {
544 if (!is_array($tags)) {
545 $tags = [$tags];
546 }
547
548 $em = $this->getDoctrine()->getManager();
549
550 foreach ($tags as $tag) {
551 if (count($tag->getEntries()) === 0) {
552 $em->remove($tag);
553 }
554 }
555
556 $em->flush();
557 }
558
769e19dc
J
559 /**
560 * Validate that the first id is equal to the second one.
4346a860 561 * If not, throw exception. It means a user try to access information from an other user.
769e19dc 562 *
4346a860 563 * @param int $requestUserId User id from the requested source
769e19dc 564 */
fcb1fba5 565 private function validateUserAccess($requestUserId)
769e19dc 566 {
18f8f32f 567 $user = $this->get('security.token_storage')->getToken()->getUser();
fcb1fba5
NL
568 if ($requestUserId != $user->getId()) {
569 throw $this->createAccessDeniedException('Access forbidden. Entry user id: '.$requestUserId.', logged user id: '.$user->getId());
769e19dc
J
570 }
571 }
7df80cb3 572}