]> git.immae.eu Git - github/wallabag/wallabag.git/blob - src/Wallabag/ApiBundle/Controller/WallabagRestController.php
Add entry export in API
[github/wallabag/wallabag.git] / src / Wallabag / ApiBundle / Controller / WallabagRestController.php
1 <?php
2
3 namespace Wallabag\ApiBundle\Controller;
4
5 use FOS\RestBundle\Controller\FOSRestController;
6 use Hateoas\Configuration\Route as HateoasRoute;
7 use Hateoas\Representation\Factory\PagerfantaFactory;
8 use Nelmio\ApiDocBundle\Annotation\ApiDoc;
9 use Symfony\Component\HttpFoundation\Request;
10 use Symfony\Component\HttpFoundation\JsonResponse;
11 use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
12 use Symfony\Component\Security\Core\Exception\AccessDeniedException;
13 use Wallabag\CoreBundle\Entity\Entry;
14 use Wallabag\CoreBundle\Entity\Tag;
15 use FOS\RestBundle\Controller\Annotations\Route;
16
17 class WallabagRestController extends FOSRestController
18 {
19 private function validateAuthentication()
20 {
21 if (false === $this->get('security.authorization_checker')->isGranted('IS_AUTHENTICATED_FULLY')) {
22 throw new AccessDeniedException();
23 }
24 }
25
26 /**
27 * Check if an entry exist by url.
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)) {
44 throw $this->createAccessDeniedException('URL is empty?, logged user id: '.$this->getUser()->getId());
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
58 /**
59 * Retrieve all entries. It could be filtered by many options.
60 *
61 * @ApiDoc(
62 * parameters={
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."},
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."},
69 * {"name"="tags", "dataType"="string", "required"=false, "format"="api,rest", "description"="a list of tags url encoded. Will returns entries that matches ALL tags."},
70 * {"name"="since", "dataType"="integer", "required"=false, "format"="default '0'", "description"="The timestamp since when you want entries updated."},
71 * }
72 * )
73 *
74 * @return JsonResponse
75 */
76 public function getEntriesAction(Request $request)
77 {
78 $this->validateAuthentication();
79
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');
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);
86 $tags = $request->query->get('tags', '');
87 $since = $request->query->get('since', 0);
88
89 $pager = $this->getDoctrine()
90 ->getRepository('WallabagCoreBundle:Entry')
91 ->findEntries($this->getUser()->getId(), $isArchived, $isStarred, $sort, $order, $since, $tags);
92
93 $pager->setCurrentPage($page);
94 $pager->setMaxPerPage($perPage);
95
96 $pagerfantaFactory = new PagerfantaFactory('page', 'perPage');
97 $paginatedCollection = $pagerfantaFactory->createRepresentation(
98 $pager,
99 new HateoasRoute(
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 )
113 );
114
115 $json = $this->get('serializer')->serialize($paginatedCollection, 'json');
116
117 return (new JsonResponse())->setJson($json);
118 }
119
120 /**
121 * Retrieve a single entry.
122 *
123 * @ApiDoc(
124 * requirements={
125 * {"name"="entry", "dataType"="integer", "requirement"="\w+", "description"="The entry ID"}
126 * }
127 * )
128 *
129 * @return JsonResponse
130 */
131 public function getEntryAction(Entry $entry)
132 {
133 $this->validateAuthentication();
134 $this->validateUserAccess($entry->getUser()->getId());
135
136 $json = $this->get('serializer')->serialize($entry, 'json');
137
138 return (new JsonResponse())->setJson($json);
139 }
140
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
165 /**
166 * Create an entry.
167 *
168 * @ApiDoc(
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."},
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"},
175 * }
176 * )
177 *
178 * @return JsonResponse
179 */
180 public function postEntriesAction(Request $request)
181 {
182 $this->validateAuthentication();
183
184 $url = $request->request->get('url');
185 $title = $request->request->get('title');
186 $isArchived = $request->request->get('archive');
187 $isStarred = $request->request->get('starred');
188
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 }
197
198 if (!is_null($title)) {
199 $entry->setTitle($title);
200 }
201
202 $tags = $request->request->get('tags', '');
203 if (!empty($tags)) {
204 $this->get('wallabag_core.content_proxy')->assignTagsToEntry($entry, $tags);
205 }
206
207 if (!is_null($isStarred)) {
208 $entry->setStarred((bool) $isStarred);
209 }
210
211 if (!is_null($isArchived)) {
212 $entry->setArchived((bool) $isArchived);
213 }
214
215 $em = $this->getDoctrine()->getManager();
216 $em->persist($entry);
217
218 $em->flush();
219
220 $json = $this->get('serializer')->serialize($entry, 'json');
221
222 return (new JsonResponse())->setJson($json);
223 }
224
225 /**
226 * Change several properties of an entry.
227 *
228 * @ApiDoc(
229 * requirements={
230 * {"name"="entry", "dataType"="integer", "requirement"="\w+", "description"="The entry ID"}
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."},
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."},
237 * }
238 * )
239 *
240 * @return JsonResponse
241 */
242 public function patchEntriesAction(Entry $entry, Request $request)
243 {
244 $this->validateAuthentication();
245 $this->validateUserAccess($entry->getUser()->getId());
246
247 $title = $request->request->get('title');
248 $isArchived = $request->request->get('archive');
249 $isStarred = $request->request->get('starred');
250
251 if (!is_null($title)) {
252 $entry->setTitle($title);
253 }
254
255 if (!is_null($isArchived)) {
256 $entry->setArchived((bool) $isArchived);
257 }
258
259 if (!is_null($isStarred)) {
260 $entry->setStarred((bool) $isStarred);
261 }
262
263 $tags = $request->request->get('tags', '');
264 if (!empty($tags)) {
265 $this->get('wallabag_core.content_proxy')->assignTagsToEntry($entry, $tags);
266 }
267
268 $em = $this->getDoctrine()->getManager();
269 $em->flush();
270
271 $json = $this->get('serializer')->serialize($entry, 'json');
272
273 return (new JsonResponse())->setJson($json);
274 }
275
276 /**
277 * Delete **permanently** an entry.
278 *
279 * @ApiDoc(
280 * requirements={
281 * {"name"="entry", "dataType"="integer", "requirement"="\w+", "description"="The entry ID"}
282 * }
283 * )
284 *
285 * @return JsonResponse
286 */
287 public function deleteEntriesAction(Entry $entry)
288 {
289 $this->validateAuthentication();
290 $this->validateUserAccess($entry->getUser()->getId());
291
292 $em = $this->getDoctrine()->getManager();
293 $em->remove($entry);
294 $em->flush();
295
296 $json = $this->get('serializer')->serialize($entry, 'json');
297
298 return (new JsonResponse())->setJson($json);
299 }
300
301 /**
302 * Retrieve all tags for an entry.
303 *
304 * @ApiDoc(
305 * requirements={
306 * {"name"="entry", "dataType"="integer", "requirement"="\w+", "description"="The entry ID"}
307 * }
308 * )
309 *
310 * @return JsonResponse
311 */
312 public function getEntriesTagsAction(Entry $entry)
313 {
314 $this->validateAuthentication();
315 $this->validateUserAccess($entry->getUser()->getId());
316
317 $json = $this->get('serializer')->serialize($entry->getTags(), 'json');
318
319 return (new JsonResponse())->setJson($json);
320 }
321
322 /**
323 * Add one or more tags to an entry.
324 *
325 * @ApiDoc(
326 * requirements={
327 * {"name"="entry", "dataType"="integer", "requirement"="\w+", "description"="The entry ID"}
328 * },
329 * parameters={
330 * {"name"="tags", "dataType"="string", "required"=false, "format"="tag1,tag2,tag3", "description"="a comma-separated list of tags."},
331 * }
332 * )
333 *
334 * @return JsonResponse
335 */
336 public function postEntriesTagsAction(Request $request, Entry $entry)
337 {
338 $this->validateAuthentication();
339 $this->validateUserAccess($entry->getUser()->getId());
340
341 $tags = $request->request->get('tags', '');
342 if (!empty($tags)) {
343 $this->get('wallabag_core.content_proxy')->assignTagsToEntry($entry, $tags);
344 }
345
346 $em = $this->getDoctrine()->getManager();
347 $em->persist($entry);
348 $em->flush();
349
350 $json = $this->get('serializer')->serialize($entry, 'json');
351
352 return (new JsonResponse())->setJson($json);
353 }
354
355 /**
356 * Permanently remove one tag for an entry.
357 *
358 * @ApiDoc(
359 * requirements={
360 * {"name"="tag", "dataType"="integer", "requirement"="\w+", "description"="The tag ID"},
361 * {"name"="entry", "dataType"="integer", "requirement"="\w+", "description"="The entry ID"}
362 * }
363 * )
364 *
365 * @return JsonResponse
366 */
367 public function deleteEntriesTagsAction(Entry $entry, Tag $tag)
368 {
369 $this->validateAuthentication();
370 $this->validateUserAccess($entry->getUser()->getId());
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
379 return (new JsonResponse())->setJson($json);
380 }
381
382 /**
383 * Retrieve all tags.
384 *
385 * @ApiDoc()
386 *
387 * @return JsonResponse
388 */
389 public function getTagsAction()
390 {
391 $this->validateAuthentication();
392
393 $tags = $this->getDoctrine()
394 ->getRepository('WallabagCoreBundle:Tag')
395 ->findAllTagsWithEntries($this->getUser()->getId());
396
397 $json = $this->get('serializer')->serialize($tags, 'json');
398
399 return (new JsonResponse())->setJson($json);
400 }
401
402 /**
403 * Permanently remove one tag from **every** entry.
404 *
405 * @ApiDoc(
406 * requirements={
407 * {"name"="tag", "dataType"="string", "required"=true, "requirement"="\w+", "description"="Tag as a string"}
408 * }
409 * )
410 *
411 * @return JsonResponse
412 */
413 public function deleteTagLabelAction(Request $request)
414 {
415 $this->validateAuthentication();
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 }
423
424 $this->getDoctrine()
425 ->getRepository('WallabagCoreBundle:Entry')
426 ->removeTag($this->getUser()->getId(), $tag);
427
428 $json = $this->get('serializer')->serialize($tag, 'json');
429
430 return (new JsonResponse())->setJson($json);
431 }
432
433 /**
434 * Permanently remove some tags from **every** entry.
435 *
436 * @ApiDoc(
437 * requirements={
438 * {"name"="tags", "dataType"="string", "required"=true, "format"="tag1,tag2", "description"="Tags as strings (comma splitted)"}
439 * }
440 * )
441 *
442 * @return JsonResponse
443 */
444 public function deleteTagsLabelAction(Request $request)
445 {
446 $this->validateAuthentication();
447
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
464 $this->getDoctrine()
465 ->getRepository('WallabagCoreBundle:Entry')
466 ->removeTags($this->getUser()->getId(), $tags);
467
468 $json = $this->get('serializer')->serialize($tags, 'json');
469
470 return (new JsonResponse())->setJson($json);
471 }
472
473 /**
474 * Permanently remove one tag from **every** entry.
475 *
476 * @ApiDoc(
477 * requirements={
478 * {"name"="tag", "dataType"="integer", "requirement"="\w+", "description"="The tag"}
479 * }
480 * )
481 *
482 * @return JsonResponse
483 */
484 public function deleteTagAction(Tag $tag)
485 {
486 $this->validateAuthentication();
487
488 $this->getDoctrine()
489 ->getRepository('WallabagCoreBundle:Entry')
490 ->removeTag($this->getUser()->getId(), $tag);
491
492 $json = $this->get('serializer')->serialize($tag, 'json');
493
494 return (new JsonResponse())->setJson($json);
495 }
496
497 /**
498 * Retrieve version number.
499 *
500 * @ApiDoc()
501 *
502 * @return JsonResponse
503 */
504 public function getVersionAction()
505 {
506 $version = $this->container->getParameter('wallabag_core.version');
507
508 $json = $this->get('serializer')->serialize($version, 'json');
509
510 return (new JsonResponse())->setJson($json);
511 }
512
513 /**
514 * Validate that the first id is equal to the second one.
515 * If not, throw exception. It means a user try to access information from an other user.
516 *
517 * @param int $requestUserId User id from the requested source
518 */
519 private function validateUserAccess($requestUserId)
520 {
521 $user = $this->get('security.token_storage')->getToken()->getUser();
522 if ($requestUserId != $user->getId()) {
523 throw $this->createAccessDeniedException('Access forbidden. Entry user id: '.$requestUserId.', logged user id: '.$user->getId());
524 }
525 }
526 }