diff options
Diffstat (limited to 'src/Wallabag/ApiBundle')
5 files changed, 511 insertions, 101 deletions
diff --git a/src/Wallabag/ApiBundle/Controller/EntryRestController.php b/src/Wallabag/ApiBundle/Controller/EntryRestController.php index 54c1747c..09b73ccb 100644 --- a/src/Wallabag/ApiBundle/Controller/EntryRestController.php +++ b/src/Wallabag/ApiBundle/Controller/EntryRestController.php | |||
@@ -5,6 +5,7 @@ namespace Wallabag\ApiBundle\Controller; | |||
5 | use Hateoas\Configuration\Route; | 5 | use Hateoas\Configuration\Route; |
6 | use Hateoas\Representation\Factory\PagerfantaFactory; | 6 | use Hateoas\Representation\Factory\PagerfantaFactory; |
7 | use Nelmio\ApiDocBundle\Annotation\ApiDoc; | 7 | use Nelmio\ApiDocBundle\Annotation\ApiDoc; |
8 | use Symfony\Component\HttpKernel\Exception\HttpException; | ||
8 | use Symfony\Component\HttpFoundation\Request; | 9 | use Symfony\Component\HttpFoundation\Request; |
9 | use Symfony\Component\HttpFoundation\JsonResponse; | 10 | use Symfony\Component\HttpFoundation\JsonResponse; |
10 | use Symfony\Component\Routing\Generator\UrlGeneratorInterface; | 11 | use Symfony\Component\Routing\Generator\UrlGeneratorInterface; |
@@ -41,12 +42,10 @@ class EntryRestController extends WallabagRestController | |||
41 | ->getRepository('WallabagCoreBundle:Entry') | 42 | ->getRepository('WallabagCoreBundle:Entry') |
42 | ->findByUrlAndUserId($url, $this->getUser()->getId()); | 43 | ->findByUrlAndUserId($url, $this->getUser()->getId()); |
43 | 44 | ||
44 | $results[$url] = false === $res ? false : true; | 45 | $results[$url] = $res instanceof Entry ? $res->getId() : false; |
45 | } | 46 | } |
46 | 47 | ||
47 | $json = $this->get('serializer')->serialize($results, 'json'); | 48 | return $this->sendResponse($results); |
48 | |||
49 | return (new JsonResponse())->setJson($json); | ||
50 | } | 49 | } |
51 | 50 | ||
52 | // let's see if it is a simple url? | 51 | // let's see if it is a simple url? |
@@ -60,11 +59,9 @@ class EntryRestController extends WallabagRestController | |||
60 | ->getRepository('WallabagCoreBundle:Entry') | 59 | ->getRepository('WallabagCoreBundle:Entry') |
61 | ->findByUrlAndUserId($url, $this->getUser()->getId()); | 60 | ->findByUrlAndUserId($url, $this->getUser()->getId()); |
62 | 61 | ||
63 | $exists = false === $res ? false : true; | 62 | $exists = $res instanceof Entry ? $res->getId() : false; |
64 | |||
65 | $json = $this->get('serializer')->serialize(['exists' => $exists], 'json'); | ||
66 | 63 | ||
67 | return (new JsonResponse())->setJson($json); | 64 | return $this->sendResponse(['exists' => $exists]); |
68 | } | 65 | } |
69 | 66 | ||
70 | /** | 67 | /** |
@@ -125,9 +122,7 @@ class EntryRestController extends WallabagRestController | |||
125 | ) | 122 | ) |
126 | ); | 123 | ); |
127 | 124 | ||
128 | $json = $this->get('serializer')->serialize($paginatedCollection, 'json'); | 125 | return $this->sendResponse($paginatedCollection); |
129 | |||
130 | return (new JsonResponse())->setJson($json); | ||
131 | } | 126 | } |
132 | 127 | ||
133 | /** | 128 | /** |
@@ -146,9 +141,7 @@ class EntryRestController extends WallabagRestController | |||
146 | $this->validateAuthentication(); | 141 | $this->validateAuthentication(); |
147 | $this->validateUserAccess($entry->getUser()->getId()); | 142 | $this->validateUserAccess($entry->getUser()->getId()); |
148 | 143 | ||
149 | $json = $this->get('serializer')->serialize($entry, 'json'); | 144 | return $this->sendResponse($entry); |
150 | |||
151 | return (new JsonResponse())->setJson($json); | ||
152 | } | 145 | } |
153 | 146 | ||
154 | /** | 147 | /** |
@@ -174,74 +167,152 @@ class EntryRestController extends WallabagRestController | |||
174 | } | 167 | } |
175 | 168 | ||
176 | /** | 169 | /** |
177 | * Create an entry. | 170 | * Handles an entries list and delete URL. |
178 | * | 171 | * |
179 | * @ApiDoc( | 172 | * @ApiDoc( |
180 | * parameters={ | 173 | * parameters={ |
181 | * {"name"="url", "dataType"="string", "required"=true, "format"="http://www.test.com/article.html", "description"="Url for the entry."}, | 174 | * {"name"="urls", "dataType"="string", "required"=true, "format"="A JSON array of urls [{'url': 'http://...'}, {'url': 'http://...'}]", "description"="Urls (as an array) to delete."} |
182 | * {"name"="title", "dataType"="string", "required"=false, "description"="Optional, we'll get the title from the page."}, | ||
183 | * {"name"="tags", "dataType"="string", "required"=false, "format"="tag1,tag2,tag3", "description"="a comma-separated list of tags."}, | ||
184 | * {"name"="starred", "dataType"="integer", "required"=false, "format"="1 or 0", "description"="entry already starred"}, | ||
185 | * {"name"="archive", "dataType"="integer", "required"=false, "format"="1 or 0", "description"="entry already archived"}, | ||
186 | * } | 175 | * } |
187 | * ) | 176 | * ) |
188 | * | 177 | * |
189 | * @return JsonResponse | 178 | * @return JsonResponse |
190 | */ | 179 | */ |
191 | public function postEntriesAction(Request $request) | 180 | public function deleteEntriesListAction(Request $request) |
192 | { | 181 | { |
193 | $this->validateAuthentication(); | 182 | $this->validateAuthentication(); |
194 | 183 | ||
195 | $url = $request->request->get('url'); | 184 | $urls = json_decode($request->query->get('urls', [])); |
196 | $title = $request->request->get('title'); | ||
197 | $isArchived = $request->request->get('archive'); | ||
198 | $isStarred = $request->request->get('starred'); | ||
199 | 185 | ||
200 | $entry = $this->get('wallabag_core.entry_repository')->findByUrlAndUserId($url, $this->getUser()->getId()); | 186 | if (empty($urls)) { |
187 | return $this->sendResponse([]); | ||
188 | } | ||
201 | 189 | ||
202 | if (false === $entry) { | 190 | $results = []; |
203 | $entry = new Entry($this->getUser()); | 191 | |
204 | try { | 192 | // handle multiple urls |
205 | $entry = $this->get('wallabag_core.content_proxy')->updateEntry( | 193 | foreach ($urls as $key => $url) { |
206 | $entry, | 194 | $entry = $this->get('wallabag_core.entry_repository')->findByUrlAndUserId( |
207 | $url | 195 | $url, |
208 | ); | 196 | $this->getUser()->getId() |
209 | } catch (\Exception $e) { | 197 | ); |
210 | $this->get('logger')->error('Error while saving an entry', [ | 198 | |
211 | 'exception' => $e, | 199 | $results[$key]['url'] = $url; |
212 | 'entry' => $entry, | 200 | |
213 | ]); | 201 | if (false !== $entry) { |
214 | $entry->setUrl($url); | 202 | $em = $this->getDoctrine()->getManager(); |
203 | $em->remove($entry); | ||
204 | $em->flush(); | ||
205 | |||
206 | // entry deleted, dispatch event about it! | ||
207 | $this->get('event_dispatcher')->dispatch(EntryDeletedEvent::NAME, new EntryDeletedEvent($entry)); | ||
215 | } | 208 | } |
216 | } | ||
217 | 209 | ||
218 | if (!is_null($title)) { | 210 | $results[$key]['entry'] = $entry instanceof Entry ? true : false; |
219 | $entry->setTitle($title); | ||
220 | } | 211 | } |
221 | 212 | ||
222 | $tags = $request->request->get('tags', ''); | 213 | return $this->sendResponse($results); |
223 | if (!empty($tags)) { | 214 | } |
224 | $this->get('wallabag_core.content_proxy')->assignTagsToEntry($entry, $tags); | 215 | |
216 | /** | ||
217 | * Handles an entries list and create URL. | ||
218 | * | ||
219 | * @ApiDoc( | ||
220 | * parameters={ | ||
221 | * {"name"="urls", "dataType"="string", "required"=true, "format"="A JSON array of urls [{'url': 'http://...'}, {'url': 'http://...'}]", "description"="Urls (as an array) to create."} | ||
222 | * } | ||
223 | * ) | ||
224 | * | ||
225 | * @return JsonResponse | ||
226 | * | ||
227 | * @throws HttpException When limit is reached | ||
228 | */ | ||
229 | public function postEntriesListAction(Request $request) | ||
230 | { | ||
231 | $this->validateAuthentication(); | ||
232 | |||
233 | $urls = json_decode($request->query->get('urls', [])); | ||
234 | |||
235 | $limit = $this->container->getParameter('wallabag_core.api_limit_mass_actions'); | ||
236 | |||
237 | if (count($urls) > $limit) { | ||
238 | throw new HttpException(400, 'API limit reached'); | ||
225 | } | 239 | } |
226 | 240 | ||
227 | if (!is_null($isStarred)) { | 241 | $results = []; |
228 | $entry->setStarred((bool) $isStarred); | 242 | if (empty($urls)) { |
243 | return $this->sendResponse($results); | ||
229 | } | 244 | } |
230 | 245 | ||
231 | if (!is_null($isArchived)) { | 246 | // handle multiple urls |
232 | $entry->setArchived((bool) $isArchived); | 247 | foreach ($urls as $key => $url) { |
248 | $entry = $this->get('wallabag_core.entry_repository')->findByUrlAndUserId( | ||
249 | $url, | ||
250 | $this->getUser()->getId() | ||
251 | ); | ||
252 | |||
253 | $results[$key]['url'] = $url; | ||
254 | |||
255 | if (false === $entry) { | ||
256 | $entry = new Entry($this->getUser()); | ||
257 | |||
258 | $this->get('wallabag_core.content_proxy')->updateEntry($entry, $url); | ||
259 | } | ||
260 | |||
261 | $em = $this->getDoctrine()->getManager(); | ||
262 | $em->persist($entry); | ||
263 | $em->flush(); | ||
264 | |||
265 | $results[$key]['entry'] = $entry instanceof Entry ? $entry->getId() : false; | ||
266 | |||
267 | // entry saved, dispatch event about it! | ||
268 | $this->get('event_dispatcher')->dispatch(EntrySavedEvent::NAME, new EntrySavedEvent($entry)); | ||
233 | } | 269 | } |
234 | 270 | ||
235 | $em = $this->getDoctrine()->getManager(); | 271 | return $this->sendResponse($results); |
236 | $em->persist($entry); | 272 | } |
237 | $em->flush(); | ||
238 | 273 | ||
239 | // entry saved, dispatch event about it! | 274 | /** |
240 | $this->get('event_dispatcher')->dispatch(EntrySavedEvent::NAME, new EntrySavedEvent($entry)); | 275 | * Create an entry. |
276 | * | ||
277 | * If you want to provide the HTML content (which means wallabag won't fetch it from the url), you must provide `content`, `title` & `url` fields **non-empty**. | ||
278 | * Otherwise, content will be fetched as normal from the url and values will be overwritten. | ||
279 | * | ||
280 | * @ApiDoc( | ||
281 | * parameters={ | ||
282 | * {"name"="url", "dataType"="string", "required"=true, "format"="http://www.test.com/article.html", "description"="Url for the entry."}, | ||
283 | * {"name"="title", "dataType"="string", "required"=false, "description"="Optional, we'll get the title from the page."}, | ||
284 | * {"name"="tags", "dataType"="string", "required"=false, "format"="tag1,tag2,tag3", "description"="a comma-separated list of tags."}, | ||
285 | * {"name"="starred", "dataType"="integer", "required"=false, "format"="1 or 0", "description"="entry already starred"}, | ||
286 | * {"name"="archive", "dataType"="integer", "required"=false, "format"="1 or 0", "description"="entry already archived"}, | ||
287 | * {"name"="content", "dataType"="string", "required"=false, "description"="Content of the entry"}, | ||
288 | * {"name"="language", "dataType"="string", "required"=false, "description"="Language of the entry"}, | ||
289 | * {"name"="preview_picture", "dataType"="string", "required"=false, "description"="Preview picture of the entry"}, | ||
290 | * {"name"="published_at", "dataType"="datetime|integer", "format"="YYYY-MM-DDTHH:II:SS+TZ or a timestamp", "required"=false, "description"="Published date of the entry"}, | ||
291 | * {"name"="authors", "dataType"="string", "format"="Name Firstname,author2,author3", "required"=false, "description"="Authors of the entry"}, | ||
292 | * } | ||
293 | * ) | ||
294 | * | ||
295 | * @return JsonResponse | ||
296 | */ | ||
297 | public function postEntriesAction(Request $request) | ||
298 | { | ||
299 | $this->validateAuthentication(); | ||
300 | |||
301 | $url = $request->request->get('url'); | ||
241 | 302 | ||
242 | $json = $this->get('serializer')->serialize($entry, 'json'); | 303 | $entry = $this->get('wallabag_core.entry_repository')->findByUrlAndUserId( |
304 | $url, | ||
305 | $this->getUser()->getId() | ||
306 | ); | ||
243 | 307 | ||
244 | return (new JsonResponse())->setJson($json); | 308 | if (false === $entry) { |
309 | $entry = new Entry($this->getUser()); | ||
310 | $entry->setUrl($url); | ||
311 | } | ||
312 | |||
313 | $this->upsertEntry($entry, $request); | ||
314 | |||
315 | return $this->sendResponse($entry); | ||
245 | } | 316 | } |
246 | 317 | ||
247 | /** | 318 | /** |
@@ -256,6 +327,11 @@ class EntryRestController extends WallabagRestController | |||
256 | * {"name"="tags", "dataType"="string", "required"=false, "format"="tag1,tag2,tag3", "description"="a comma-separated list of tags."}, | 327 | * {"name"="tags", "dataType"="string", "required"=false, "format"="tag1,tag2,tag3", "description"="a comma-separated list of tags."}, |
257 | * {"name"="archive", "dataType"="integer", "required"=false, "format"="1 or 0", "description"="archived the entry."}, | 328 | * {"name"="archive", "dataType"="integer", "required"=false, "format"="1 or 0", "description"="archived the entry."}, |
258 | * {"name"="starred", "dataType"="integer", "required"=false, "format"="1 or 0", "description"="starred the entry."}, | 329 | * {"name"="starred", "dataType"="integer", "required"=false, "format"="1 or 0", "description"="starred the entry."}, |
330 | * {"name"="content", "dataType"="string", "required"=false, "description"="Content of the entry"}, | ||
331 | * {"name"="language", "dataType"="string", "required"=false, "description"="Language of the entry"}, | ||
332 | * {"name"="preview_picture", "dataType"="string", "required"=false, "description"="Preview picture of the entry"}, | ||
333 | * {"name"="published_at", "dataType"="datetime|integer", "format"="YYYY-MM-DDTHH:II:SS+TZ or a timestamp", "required"=false, "description"="Published date of the entry"}, | ||
334 | * {"name"="authors", "dataType"="string", "format"="Name Firstname,author2,author3", "required"=false, "description"="Authors of the entry"}, | ||
259 | * } | 335 | * } |
260 | * ) | 336 | * ) |
261 | * | 337 | * |
@@ -266,33 +342,9 @@ class EntryRestController extends WallabagRestController | |||
266 | $this->validateAuthentication(); | 342 | $this->validateAuthentication(); |
267 | $this->validateUserAccess($entry->getUser()->getId()); | 343 | $this->validateUserAccess($entry->getUser()->getId()); |
268 | 344 | ||
269 | $title = $request->request->get('title'); | 345 | $this->upsertEntry($entry, $request, true); |
270 | $isArchived = $request->request->get('archive'); | ||
271 | $isStarred = $request->request->get('starred'); | ||
272 | 346 | ||
273 | if (!is_null($title)) { | 347 | return $this->sendResponse($entry); |
274 | $entry->setTitle($title); | ||
275 | } | ||
276 | |||
277 | if (!is_null($isArchived)) { | ||
278 | $entry->setArchived((bool) $isArchived); | ||
279 | } | ||
280 | |||
281 | if (!is_null($isStarred)) { | ||
282 | $entry->setStarred((bool) $isStarred); | ||
283 | } | ||
284 | |||
285 | $tags = $request->request->get('tags', ''); | ||
286 | if (!empty($tags)) { | ||
287 | $this->get('wallabag_core.content_proxy')->assignTagsToEntry($entry, $tags); | ||
288 | } | ||
289 | |||
290 | $em = $this->getDoctrine()->getManager(); | ||
291 | $em->flush(); | ||
292 | |||
293 | $json = $this->get('serializer')->serialize($entry, 'json'); | ||
294 | |||
295 | return (new JsonResponse())->setJson($json); | ||
296 | } | 348 | } |
297 | 349 | ||
298 | /** | 350 | /** |
@@ -313,7 +365,7 @@ class EntryRestController extends WallabagRestController | |||
313 | $this->validateUserAccess($entry->getUser()->getId()); | 365 | $this->validateUserAccess($entry->getUser()->getId()); |
314 | 366 | ||
315 | try { | 367 | try { |
316 | $entry = $this->get('wallabag_core.content_proxy')->updateEntry($entry, $entry->getUrl()); | 368 | $this->get('wallabag_core.content_proxy')->updateEntry($entry, $entry->getUrl()); |
317 | } catch (\Exception $e) { | 369 | } catch (\Exception $e) { |
318 | $this->get('logger')->error('Error while saving an entry', [ | 370 | $this->get('logger')->error('Error while saving an entry', [ |
319 | 'exception' => $e, | 371 | 'exception' => $e, |
@@ -335,9 +387,7 @@ class EntryRestController extends WallabagRestController | |||
335 | // entry saved, dispatch event about it! | 387 | // entry saved, dispatch event about it! |
336 | $this->get('event_dispatcher')->dispatch(EntrySavedEvent::NAME, new EntrySavedEvent($entry)); | 388 | $this->get('event_dispatcher')->dispatch(EntrySavedEvent::NAME, new EntrySavedEvent($entry)); |
337 | 389 | ||
338 | $json = $this->get('serializer')->serialize($entry, 'json'); | 390 | return $this->sendResponse($entry); |
339 | |||
340 | return (new JsonResponse())->setJson($json); | ||
341 | } | 391 | } |
342 | 392 | ||
343 | /** | 393 | /** |
@@ -363,9 +413,7 @@ class EntryRestController extends WallabagRestController | |||
363 | // entry deleted, dispatch event about it! | 413 | // entry deleted, dispatch event about it! |
364 | $this->get('event_dispatcher')->dispatch(EntryDeletedEvent::NAME, new EntryDeletedEvent($entry)); | 414 | $this->get('event_dispatcher')->dispatch(EntryDeletedEvent::NAME, new EntryDeletedEvent($entry)); |
365 | 415 | ||
366 | $json = $this->get('serializer')->serialize($entry, 'json'); | 416 | return $this->sendResponse($entry); |
367 | |||
368 | return (new JsonResponse())->setJson($json); | ||
369 | } | 417 | } |
370 | 418 | ||
371 | /** | 419 | /** |
@@ -384,9 +432,7 @@ class EntryRestController extends WallabagRestController | |||
384 | $this->validateAuthentication(); | 432 | $this->validateAuthentication(); |
385 | $this->validateUserAccess($entry->getUser()->getId()); | 433 | $this->validateUserAccess($entry->getUser()->getId()); |
386 | 434 | ||
387 | $json = $this->get('serializer')->serialize($entry->getTags(), 'json'); | 435 | return $this->sendResponse($entry->getTags()); |
388 | |||
389 | return (new JsonResponse())->setJson($json); | ||
390 | } | 436 | } |
391 | 437 | ||
392 | /** | 438 | /** |
@@ -410,16 +456,14 @@ class EntryRestController extends WallabagRestController | |||
410 | 456 | ||
411 | $tags = $request->request->get('tags', ''); | 457 | $tags = $request->request->get('tags', ''); |
412 | if (!empty($tags)) { | 458 | if (!empty($tags)) { |
413 | $this->get('wallabag_core.content_proxy')->assignTagsToEntry($entry, $tags); | 459 | $this->get('wallabag_core.tags_assigner')->assignTagsToEntry($entry, $tags); |
414 | } | 460 | } |
415 | 461 | ||
416 | $em = $this->getDoctrine()->getManager(); | 462 | $em = $this->getDoctrine()->getManager(); |
417 | $em->persist($entry); | 463 | $em->persist($entry); |
418 | $em->flush(); | 464 | $em->flush(); |
419 | 465 | ||
420 | $json = $this->get('serializer')->serialize($entry, 'json'); | 466 | return $this->sendResponse($entry); |
421 | |||
422 | return (new JsonResponse())->setJson($json); | ||
423 | } | 467 | } |
424 | 468 | ||
425 | /** | 469 | /** |
@@ -444,8 +488,189 @@ class EntryRestController extends WallabagRestController | |||
444 | $em->persist($entry); | 488 | $em->persist($entry); |
445 | $em->flush(); | 489 | $em->flush(); |
446 | 490 | ||
447 | $json = $this->get('serializer')->serialize($entry, 'json'); | 491 | return $this->sendResponse($entry); |
492 | } | ||
493 | |||
494 | /** | ||
495 | * Handles an entries list delete tags from them. | ||
496 | * | ||
497 | * @ApiDoc( | ||
498 | * parameters={ | ||
499 | * {"name"="list", "dataType"="string", "required"=true, "format"="A JSON array of urls [{'url': 'http://...','tags': 'tag1, tag2'}, {'url': 'http://...','tags': 'tag1, tag2'}]", "description"="Urls (as an array) to handle."} | ||
500 | * } | ||
501 | * ) | ||
502 | * | ||
503 | * @return JsonResponse | ||
504 | */ | ||
505 | public function deleteEntriesTagsListAction(Request $request) | ||
506 | { | ||
507 | $this->validateAuthentication(); | ||
508 | |||
509 | $list = json_decode($request->query->get('list', [])); | ||
510 | |||
511 | if (empty($list)) { | ||
512 | return $this->sendResponse([]); | ||
513 | } | ||
514 | |||
515 | // handle multiple urls | ||
516 | $results = []; | ||
517 | |||
518 | foreach ($list as $key => $element) { | ||
519 | $entry = $this->get('wallabag_core.entry_repository')->findByUrlAndUserId( | ||
520 | $element->url, | ||
521 | $this->getUser()->getId() | ||
522 | ); | ||
523 | |||
524 | $results[$key]['url'] = $element->url; | ||
525 | $results[$key]['entry'] = $entry instanceof Entry ? $entry->getId() : false; | ||
526 | |||
527 | $tags = $element->tags; | ||
528 | |||
529 | if (false !== $entry && !(empty($tags))) { | ||
530 | $tags = explode(',', $tags); | ||
531 | foreach ($tags as $label) { | ||
532 | $label = trim($label); | ||
533 | |||
534 | $tag = $this->getDoctrine() | ||
535 | ->getRepository('WallabagCoreBundle:Tag') | ||
536 | ->findOneByLabel($label); | ||
537 | |||
538 | if (false !== $tag) { | ||
539 | $entry->removeTag($tag); | ||
540 | } | ||
541 | } | ||
542 | |||
543 | $em = $this->getDoctrine()->getManager(); | ||
544 | $em->persist($entry); | ||
545 | $em->flush(); | ||
546 | } | ||
547 | } | ||
548 | |||
549 | return $this->sendResponse($results); | ||
550 | } | ||
551 | |||
552 | /** | ||
553 | * Handles an entries list and add tags to them. | ||
554 | * | ||
555 | * @ApiDoc( | ||
556 | * parameters={ | ||
557 | * {"name"="list", "dataType"="string", "required"=true, "format"="A JSON array of urls [{'url': 'http://...','tags': 'tag1, tag2'}, {'url': 'http://...','tags': 'tag1, tag2'}]", "description"="Urls (as an array) to handle."} | ||
558 | * } | ||
559 | * ) | ||
560 | * | ||
561 | * @return JsonResponse | ||
562 | */ | ||
563 | public function postEntriesTagsListAction(Request $request) | ||
564 | { | ||
565 | $this->validateAuthentication(); | ||
566 | |||
567 | $list = json_decode($request->query->get('list', [])); | ||
568 | |||
569 | if (empty($list)) { | ||
570 | return $this->sendResponse([]); | ||
571 | } | ||
572 | |||
573 | $results = []; | ||
574 | |||
575 | // handle multiple urls | ||
576 | foreach ($list as $key => $element) { | ||
577 | $entry = $this->get('wallabag_core.entry_repository')->findByUrlAndUserId( | ||
578 | $element->url, | ||
579 | $this->getUser()->getId() | ||
580 | ); | ||
581 | |||
582 | $results[$key]['url'] = $element->url; | ||
583 | $results[$key]['entry'] = $entry instanceof Entry ? $entry->getId() : false; | ||
584 | |||
585 | $tags = $element->tags; | ||
586 | |||
587 | if (false !== $entry && !(empty($tags))) { | ||
588 | $this->get('wallabag_core.tags_assigner')->assignTagsToEntry($entry, $tags); | ||
589 | |||
590 | $em = $this->getDoctrine()->getManager(); | ||
591 | $em->persist($entry); | ||
592 | $em->flush(); | ||
593 | } | ||
594 | } | ||
595 | |||
596 | return $this->sendResponse($results); | ||
597 | } | ||
598 | |||
599 | /** | ||
600 | * Shortcut to send data serialized in json. | ||
601 | * | ||
602 | * @param mixed $data | ||
603 | * | ||
604 | * @return JsonResponse | ||
605 | */ | ||
606 | private function sendResponse($data) | ||
607 | { | ||
608 | $json = $this->get('serializer')->serialize($data, 'json'); | ||
448 | 609 | ||
449 | return (new JsonResponse())->setJson($json); | 610 | return (new JsonResponse())->setJson($json); |
450 | } | 611 | } |
612 | |||
613 | /** | ||
614 | * Update or Insert a new entry. | ||
615 | * | ||
616 | * @param Entry $entry | ||
617 | * @param Request $request | ||
618 | * @param bool $disableContentUpdate If we don't want the content to be update by fetching the url (used when patching instead of posting) | ||
619 | */ | ||
620 | private function upsertEntry(Entry $entry, Request $request, $disableContentUpdate = false) | ||
621 | { | ||
622 | $title = $request->request->get('title'); | ||
623 | $tags = $request->request->get('tags', []); | ||
624 | $isArchived = $request->request->get('archive'); | ||
625 | $isStarred = $request->request->get('starred'); | ||
626 | $content = $request->request->get('content'); | ||
627 | $language = $request->request->get('language'); | ||
628 | $picture = $request->request->get('preview_picture'); | ||
629 | $publishedAt = $request->request->get('published_at'); | ||
630 | $authors = $request->request->get('authors', ''); | ||
631 | |||
632 | try { | ||
633 | $this->get('wallabag_core.content_proxy')->updateEntry( | ||
634 | $entry, | ||
635 | $entry->getUrl(), | ||
636 | [ | ||
637 | 'title' => !empty($title) ? $title : $entry->getTitle(), | ||
638 | 'html' => !empty($content) ? $content : $entry->getContent(), | ||
639 | 'url' => $entry->getUrl(), | ||
640 | 'language' => !empty($language) ? $language : $entry->getLanguage(), | ||
641 | 'date' => !empty($publishedAt) ? $publishedAt : $entry->getPublishedAt(), | ||
642 | // faking the open graph preview picture | ||
643 | 'open_graph' => [ | ||
644 | 'og_image' => !empty($picture) ? $picture : $entry->getPreviewPicture(), | ||
645 | ], | ||
646 | 'authors' => is_string($authors) ? explode(',', $authors) : $entry->getPublishedBy(), | ||
647 | ], | ||
648 | $disableContentUpdate | ||
649 | ); | ||
650 | } catch (\Exception $e) { | ||
651 | $this->get('logger')->error('Error while saving an entry', [ | ||
652 | 'exception' => $e, | ||
653 | 'entry' => $entry, | ||
654 | ]); | ||
655 | } | ||
656 | |||
657 | if (!is_null($isArchived)) { | ||
658 | $entry->setArchived((bool) $isArchived); | ||
659 | } | ||
660 | |||
661 | if (!is_null($isStarred)) { | ||
662 | $entry->setStarred((bool) $isStarred); | ||
663 | } | ||
664 | |||
665 | if (!empty($tags)) { | ||
666 | $this->get('wallabag_core.tags_assigner')->assignTagsToEntry($entry, $tags); | ||
667 | } | ||
668 | |||
669 | $em = $this->getDoctrine()->getManager(); | ||
670 | $em->persist($entry); | ||
671 | $em->flush(); | ||
672 | |||
673 | // entry saved, dispatch event about it! | ||
674 | $this->get('event_dispatcher')->dispatch(EntrySavedEvent::NAME, new EntrySavedEvent($entry)); | ||
675 | } | ||
451 | } | 676 | } |
diff --git a/src/Wallabag/ApiBundle/Controller/TagRestController.php b/src/Wallabag/ApiBundle/Controller/TagRestController.php index bc6d4e64..354187a0 100644 --- a/src/Wallabag/ApiBundle/Controller/TagRestController.php +++ b/src/Wallabag/ApiBundle/Controller/TagRestController.php | |||
@@ -31,7 +31,7 @@ class TagRestController extends WallabagRestController | |||
31 | } | 31 | } |
32 | 32 | ||
33 | /** | 33 | /** |
34 | * Permanently remove one tag from **every** entry. | 34 | * Permanently remove one tag from **every** entry by passing the Tag label. |
35 | * | 35 | * |
36 | * @ApiDoc( | 36 | * @ApiDoc( |
37 | * requirements={ | 37 | * requirements={ |
@@ -44,7 +44,7 @@ class TagRestController extends WallabagRestController | |||
44 | public function deleteTagLabelAction(Request $request) | 44 | public function deleteTagLabelAction(Request $request) |
45 | { | 45 | { |
46 | $this->validateAuthentication(); | 46 | $this->validateAuthentication(); |
47 | $label = $request->request->get('tag', ''); | 47 | $label = $request->get('tag', ''); |
48 | 48 | ||
49 | $tag = $this->getDoctrine()->getRepository('WallabagCoreBundle:Tag')->findOneByLabel($label); | 49 | $tag = $this->getDoctrine()->getRepository('WallabagCoreBundle:Tag')->findOneByLabel($label); |
50 | 50 | ||
@@ -78,7 +78,7 @@ class TagRestController extends WallabagRestController | |||
78 | { | 78 | { |
79 | $this->validateAuthentication(); | 79 | $this->validateAuthentication(); |
80 | 80 | ||
81 | $tagsLabels = $request->request->get('tags', ''); | 81 | $tagsLabels = $request->get('tags', ''); |
82 | 82 | ||
83 | $tags = []; | 83 | $tags = []; |
84 | 84 | ||
@@ -106,7 +106,7 @@ class TagRestController extends WallabagRestController | |||
106 | } | 106 | } |
107 | 107 | ||
108 | /** | 108 | /** |
109 | * Permanently remove one tag from **every** entry. | 109 | * Permanently remove one tag from **every** entry by passing the Tag ID. |
110 | * | 110 | * |
111 | * @ApiDoc( | 111 | * @ApiDoc( |
112 | * requirements={ | 112 | * requirements={ |
diff --git a/src/Wallabag/ApiBundle/Controller/UserRestController.php b/src/Wallabag/ApiBundle/Controller/UserRestController.php new file mode 100644 index 00000000..7471f5f6 --- /dev/null +++ b/src/Wallabag/ApiBundle/Controller/UserRestController.php | |||
@@ -0,0 +1,157 @@ | |||
1 | <?php | ||
2 | |||
3 | namespace Wallabag\ApiBundle\Controller; | ||
4 | |||
5 | use FOS\UserBundle\Event\UserEvent; | ||
6 | use FOS\UserBundle\FOSUserEvents; | ||
7 | use JMS\Serializer\SerializationContext; | ||
8 | use Nelmio\ApiDocBundle\Annotation\ApiDoc; | ||
9 | use Symfony\Component\HttpFoundation\Request; | ||
10 | use Symfony\Component\HttpFoundation\JsonResponse; | ||
11 | use Wallabag\UserBundle\Entity\User; | ||
12 | use Wallabag\ApiBundle\Entity\Client; | ||
13 | |||
14 | class UserRestController extends WallabagRestController | ||
15 | { | ||
16 | /** | ||
17 | * Retrieve current logged in user informations. | ||
18 | * | ||
19 | * @ApiDoc() | ||
20 | * | ||
21 | * @return JsonResponse | ||
22 | */ | ||
23 | public function getUserAction() | ||
24 | { | ||
25 | $this->validateAuthentication(); | ||
26 | |||
27 | return $this->sendUser($this->getUser()); | ||
28 | } | ||
29 | |||
30 | /** | ||
31 | * Register an user and create a client. | ||
32 | * | ||
33 | * @ApiDoc( | ||
34 | * requirements={ | ||
35 | * {"name"="username", "dataType"="string", "required"=true, "description"="The user's username"}, | ||
36 | * {"name"="password", "dataType"="string", "required"=true, "description"="The user's password"}, | ||
37 | * {"name"="email", "dataType"="string", "required"=true, "description"="The user's email"}, | ||
38 | * {"name"="client_name", "dataType"="string", "required"=true, "description"="The client name (to be used by your app)"} | ||
39 | * } | ||
40 | * ) | ||
41 | * | ||
42 | * @todo Make this method (or the whole API) accessible only through https | ||
43 | * | ||
44 | * @return JsonResponse | ||
45 | */ | ||
46 | public function putUserAction(Request $request) | ||
47 | { | ||
48 | if (!$this->getParameter('fosuser_registration') || !$this->get('craue_config')->get('api_user_registration')) { | ||
49 | $json = $this->get('serializer')->serialize(['error' => "Server doesn't allow registrations"], 'json'); | ||
50 | |||
51 | return (new JsonResponse()) | ||
52 | ->setJson($json) | ||
53 | ->setStatusCode(JsonResponse::HTTP_FORBIDDEN); | ||
54 | } | ||
55 | |||
56 | $userManager = $this->get('fos_user.user_manager'); | ||
57 | $user = $userManager->createUser(); | ||
58 | // user will be disabled BY DEFAULT to avoid spamming account to be enabled | ||
59 | $user->setEnabled(false); | ||
60 | |||
61 | $form = $this->createForm('Wallabag\UserBundle\Form\NewUserType', $user, [ | ||
62 | 'csrf_protection' => false, | ||
63 | ]); | ||
64 | |||
65 | // simulate form submission | ||
66 | $form->submit([ | ||
67 | 'username' => $request->request->get('username'), | ||
68 | 'plainPassword' => [ | ||
69 | 'first' => $request->request->get('password'), | ||
70 | 'second' => $request->request->get('password'), | ||
71 | ], | ||
72 | 'email' => $request->request->get('email'), | ||
73 | ]); | ||
74 | |||
75 | if ($form->isSubmitted() && false === $form->isValid()) { | ||
76 | $view = $this->view($form, 400); | ||
77 | $view->setFormat('json'); | ||
78 | |||
79 | // handle errors in a more beautiful way than the default view | ||
80 | $data = json_decode($this->handleView($view)->getContent(), true)['children']; | ||
81 | $errors = []; | ||
82 | |||
83 | if (isset($data['username']['errors'])) { | ||
84 | $errors['username'] = $this->translateErrors($data['username']['errors']); | ||
85 | } | ||
86 | |||
87 | if (isset($data['email']['errors'])) { | ||
88 | $errors['email'] = $this->translateErrors($data['email']['errors']); | ||
89 | } | ||
90 | |||
91 | if (isset($data['plainPassword']['children']['first']['errors'])) { | ||
92 | $errors['password'] = $this->translateErrors($data['plainPassword']['children']['first']['errors']); | ||
93 | } | ||
94 | |||
95 | $json = $this->get('serializer')->serialize(['error' => $errors], 'json'); | ||
96 | |||
97 | return (new JsonResponse()) | ||
98 | ->setJson($json) | ||
99 | ->setStatusCode(JsonResponse::HTTP_BAD_REQUEST); | ||
100 | } | ||
101 | |||
102 | // create a default client | ||
103 | $client = new Client($user); | ||
104 | $client->setName($request->request->get('client_name', 'Default client')); | ||
105 | |||
106 | $this->getDoctrine()->getManager()->persist($client); | ||
107 | |||
108 | $user->addClient($client); | ||
109 | |||
110 | $userManager->updateUser($user); | ||
111 | |||
112 | // dispatch a created event so the associated config will be created | ||
113 | $event = new UserEvent($user, $request); | ||
114 | $this->get('event_dispatcher')->dispatch(FOSUserEvents::USER_CREATED, $event); | ||
115 | |||
116 | return $this->sendUser($user, 'user_api_with_client', JsonResponse::HTTP_CREATED); | ||
117 | } | ||
118 | |||
119 | /** | ||
120 | * Send user response. | ||
121 | * | ||
122 | * @param User $user | ||
123 | * @param string $group Used to define with serialized group might be used | ||
124 | * @param int $status HTTP Status code to send | ||
125 | * | ||
126 | * @return JsonResponse | ||
127 | */ | ||
128 | private function sendUser(User $user, $group = 'user_api', $status = JsonResponse::HTTP_OK) | ||
129 | { | ||
130 | $json = $this->get('serializer')->serialize( | ||
131 | $user, | ||
132 | 'json', | ||
133 | SerializationContext::create()->setGroups([$group]) | ||
134 | ); | ||
135 | |||
136 | return (new JsonResponse()) | ||
137 | ->setJson($json) | ||
138 | ->setStatusCode($status); | ||
139 | } | ||
140 | |||
141 | /** | ||
142 | * Translate errors message. | ||
143 | * | ||
144 | * @param array $errors | ||
145 | * | ||
146 | * @return array | ||
147 | */ | ||
148 | private function translateErrors($errors) | ||
149 | { | ||
150 | $translatedErrors = []; | ||
151 | foreach ($errors as $error) { | ||
152 | $translatedErrors[] = $this->get('translator')->trans($error); | ||
153 | } | ||
154 | |||
155 | return $translatedErrors; | ||
156 | } | ||
157 | } | ||
diff --git a/src/Wallabag/ApiBundle/Entity/Client.php b/src/Wallabag/ApiBundle/Entity/Client.php index 9ed9f980..c15fd3fa 100644 --- a/src/Wallabag/ApiBundle/Entity/Client.php +++ b/src/Wallabag/ApiBundle/Entity/Client.php | |||
@@ -5,6 +5,9 @@ namespace Wallabag\ApiBundle\Entity; | |||
5 | use Doctrine\ORM\Mapping as ORM; | 5 | use Doctrine\ORM\Mapping as ORM; |
6 | use FOS\OAuthServerBundle\Entity\Client as BaseClient; | 6 | use FOS\OAuthServerBundle\Entity\Client as BaseClient; |
7 | use Wallabag\UserBundle\Entity\User; | 7 | use Wallabag\UserBundle\Entity\User; |
8 | use JMS\Serializer\Annotation\Groups; | ||
9 | use JMS\Serializer\Annotation\SerializedName; | ||
10 | use JMS\Serializer\Annotation\VirtualProperty; | ||
8 | 11 | ||
9 | /** | 12 | /** |
10 | * @ORM\Table("oauth2_clients") | 13 | * @ORM\Table("oauth2_clients") |
@@ -23,6 +26,8 @@ class Client extends BaseClient | |||
23 | * @var string | 26 | * @var string |
24 | * | 27 | * |
25 | * @ORM\Column(name="name", type="text", nullable=false) | 28 | * @ORM\Column(name="name", type="text", nullable=false) |
29 | * | ||
30 | * @Groups({"user_api_with_client"}) | ||
26 | */ | 31 | */ |
27 | protected $name; | 32 | protected $name; |
28 | 33 | ||
@@ -37,6 +42,14 @@ class Client extends BaseClient | |||
37 | protected $accessTokens; | 42 | protected $accessTokens; |
38 | 43 | ||
39 | /** | 44 | /** |
45 | * @var string | ||
46 | * | ||
47 | * @SerializedName("client_secret") | ||
48 | * @Groups({"user_api_with_client"}) | ||
49 | */ | ||
50 | protected $secret; | ||
51 | |||
52 | /** | ||
40 | * @ORM\ManyToOne(targetEntity="Wallabag\UserBundle\Entity\User", inversedBy="clients") | 53 | * @ORM\ManyToOne(targetEntity="Wallabag\UserBundle\Entity\User", inversedBy="clients") |
41 | */ | 54 | */ |
42 | private $user; | 55 | private $user; |
@@ -78,4 +91,14 @@ class Client extends BaseClient | |||
78 | { | 91 | { |
79 | return $this->user; | 92 | return $this->user; |
80 | } | 93 | } |
94 | |||
95 | /** | ||
96 | * @VirtualProperty | ||
97 | * @SerializedName("client_id") | ||
98 | * @Groups({"user_api_with_client"}) | ||
99 | */ | ||
100 | public function getClientId() | ||
101 | { | ||
102 | return $this->getId().'_'.$this->getRandomId(); | ||
103 | } | ||
81 | } | 104 | } |
diff --git a/src/Wallabag/ApiBundle/Resources/config/routing_rest.yml b/src/Wallabag/ApiBundle/Resources/config/routing_rest.yml index 57d37f4b..c0283e71 100644 --- a/src/Wallabag/ApiBundle/Resources/config/routing_rest.yml +++ b/src/Wallabag/ApiBundle/Resources/config/routing_rest.yml | |||
@@ -17,3 +17,8 @@ misc: | |||
17 | type: rest | 17 | type: rest |
18 | resource: "WallabagApiBundle:WallabagRest" | 18 | resource: "WallabagApiBundle:WallabagRest" |
19 | name_prefix: api_ | 19 | name_prefix: api_ |
20 | |||
21 | user: | ||
22 | type: rest | ||
23 | resource: "WallabagApiBundle:UserRest" | ||
24 | name_prefix: api_ | ||