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