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