diff options
Diffstat (limited to 'src/Wallabag/ApiBundle')
9 files changed, 624 insertions, 90 deletions
diff --git a/src/Wallabag/ApiBundle/Controller/AnnotationRestController.php b/src/Wallabag/ApiBundle/Controller/AnnotationRestController.php index 2dd26c07..28d55ba9 100644 --- a/src/Wallabag/ApiBundle/Controller/AnnotationRestController.php +++ b/src/Wallabag/ApiBundle/Controller/AnnotationRestController.php | |||
@@ -4,10 +4,10 @@ namespace Wallabag\ApiBundle\Controller; | |||
4 | 4 | ||
5 | use Nelmio\ApiDocBundle\Annotation\ApiDoc; | 5 | use Nelmio\ApiDocBundle\Annotation\ApiDoc; |
6 | use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter; | 6 | use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter; |
7 | use Symfony\Component\HttpFoundation\Request; | ||
8 | use Symfony\Component\HttpFoundation\JsonResponse; | 7 | use Symfony\Component\HttpFoundation\JsonResponse; |
9 | use Wallabag\CoreBundle\Entity\Entry; | 8 | use Symfony\Component\HttpFoundation\Request; |
10 | use Wallabag\AnnotationBundle\Entity\Annotation; | 9 | use Wallabag\AnnotationBundle\Entity\Annotation; |
10 | use Wallabag\CoreBundle\Entity\Entry; | ||
11 | 11 | ||
12 | class AnnotationRestController extends WallabagRestController | 12 | class AnnotationRestController extends WallabagRestController |
13 | { | 13 | { |
diff --git a/src/Wallabag/ApiBundle/Controller/DeveloperController.php b/src/Wallabag/ApiBundle/Controller/DeveloperController.php index 9cb1b626..c7178017 100644 --- a/src/Wallabag/ApiBundle/Controller/DeveloperController.php +++ b/src/Wallabag/ApiBundle/Controller/DeveloperController.php | |||
@@ -3,8 +3,8 @@ | |||
3 | namespace Wallabag\ApiBundle\Controller; | 3 | namespace Wallabag\ApiBundle\Controller; |
4 | 4 | ||
5 | use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route; | 5 | use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route; |
6 | use Symfony\Component\HttpFoundation\Request; | ||
7 | use Symfony\Bundle\FrameworkBundle\Controller\Controller; | 6 | use Symfony\Bundle\FrameworkBundle\Controller\Controller; |
7 | use Symfony\Component\HttpFoundation\Request; | ||
8 | use Wallabag\ApiBundle\Entity\Client; | 8 | use Wallabag\ApiBundle\Entity\Client; |
9 | use Wallabag\ApiBundle\Form\Type\ClientType; | 9 | use Wallabag\ApiBundle\Form\Type\ClientType; |
10 | 10 | ||
@@ -75,7 +75,7 @@ class DeveloperController extends Controller | |||
75 | */ | 75 | */ |
76 | public function deleteClientAction(Client $client) | 76 | public function deleteClientAction(Client $client) |
77 | { | 77 | { |
78 | if (null === $this->getUser() || $client->getUser()->getId() != $this->getUser()->getId()) { | 78 | if (null === $this->getUser() || $client->getUser()->getId() !== $this->getUser()->getId()) { |
79 | throw $this->createAccessDeniedException('You can not access this client.'); | 79 | throw $this->createAccessDeniedException('You can not access this client.'); |
80 | } | 80 | } |
81 | 81 | ||
diff --git a/src/Wallabag/ApiBundle/Controller/EntryRestController.php b/src/Wallabag/ApiBundle/Controller/EntryRestController.php index 54c1747c..bc1b6f92 100644 --- a/src/Wallabag/ApiBundle/Controller/EntryRestController.php +++ b/src/Wallabag/ApiBundle/Controller/EntryRestController.php | |||
@@ -4,22 +4,30 @@ namespace Wallabag\ApiBundle\Controller; | |||
4 | 4 | ||
5 | use Hateoas\Configuration\Route; | 5 | use Hateoas\Configuration\Route; |
6 | use Hateoas\Representation\Factory\PagerfantaFactory; | 6 | use Hateoas\Representation\Factory\PagerfantaFactory; |
7 | use JMS\Serializer\SerializationContext; | ||
7 | use Nelmio\ApiDocBundle\Annotation\ApiDoc; | 8 | use Nelmio\ApiDocBundle\Annotation\ApiDoc; |
8 | use Symfony\Component\HttpFoundation\Request; | ||
9 | use Symfony\Component\HttpFoundation\JsonResponse; | 9 | use Symfony\Component\HttpFoundation\JsonResponse; |
10 | use Symfony\Component\HttpFoundation\Request; | ||
11 | use Symfony\Component\HttpFoundation\Response; | ||
12 | use Symfony\Component\HttpKernel\Exception\HttpException; | ||
10 | use Symfony\Component\Routing\Generator\UrlGeneratorInterface; | 13 | use Symfony\Component\Routing\Generator\UrlGeneratorInterface; |
11 | use Wallabag\CoreBundle\Entity\Entry; | 14 | use Wallabag\CoreBundle\Entity\Entry; |
12 | use Wallabag\CoreBundle\Entity\Tag; | 15 | use Wallabag\CoreBundle\Entity\Tag; |
13 | use Wallabag\CoreBundle\Event\EntrySavedEvent; | ||
14 | use Wallabag\CoreBundle\Event\EntryDeletedEvent; | 16 | use Wallabag\CoreBundle\Event\EntryDeletedEvent; |
17 | use Wallabag\CoreBundle\Event\EntrySavedEvent; | ||
15 | 18 | ||
16 | class EntryRestController extends WallabagRestController | 19 | class EntryRestController extends WallabagRestController |
17 | { | 20 | { |
18 | /** | 21 | /** |
19 | * Check if an entry exist by url. | 22 | * Check if an entry exist by url. |
23 | * Return ID if entry(ies) exist (and if you give the return_id parameter). | ||
24 | * Otherwise it returns false. | ||
25 | * | ||
26 | * @todo Remove that `return_id` in the next major release | ||
20 | * | 27 | * |
21 | * @ApiDoc( | 28 | * @ApiDoc( |
22 | * parameters={ | 29 | * parameters={ |
30 | * {"name"="return_id", "dataType"="string", "required"=false, "format"="1 or 0", "description"="Set 1 if you want to retrieve ID in case entry(ies) exists, 0 by default"}, | ||
23 | * {"name"="url", "dataType"="string", "required"=true, "format"="An url", "description"="Url to check if it exists"}, | 31 | * {"name"="url", "dataType"="string", "required"=true, "format"="An url", "description"="Url to check if it exists"}, |
24 | * {"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"} | 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"} |
25 | * } | 33 | * } |
@@ -31,6 +39,7 @@ class EntryRestController extends WallabagRestController | |||
31 | { | 39 | { |
32 | $this->validateAuthentication(); | 40 | $this->validateAuthentication(); |
33 | 41 | ||
42 | $returnId = (null === $request->query->get('return_id')) ? false : (bool) $request->query->get('return_id'); | ||
34 | $urls = $request->query->get('urls', []); | 43 | $urls = $request->query->get('urls', []); |
35 | 44 | ||
36 | // handle multiple urls first | 45 | // handle multiple urls first |
@@ -41,30 +50,26 @@ class EntryRestController extends WallabagRestController | |||
41 | ->getRepository('WallabagCoreBundle:Entry') | 50 | ->getRepository('WallabagCoreBundle:Entry') |
42 | ->findByUrlAndUserId($url, $this->getUser()->getId()); | 51 | ->findByUrlAndUserId($url, $this->getUser()->getId()); |
43 | 52 | ||
44 | $results[$url] = false === $res ? false : true; | 53 | $results[$url] = $this->returnExistInformation($res, $returnId); |
45 | } | 54 | } |
46 | 55 | ||
47 | $json = $this->get('serializer')->serialize($results, 'json'); | 56 | return $this->sendResponse($results); |
48 | |||
49 | return (new JsonResponse())->setJson($json); | ||
50 | } | 57 | } |
51 | 58 | ||
52 | // let's see if it is a simple url? | 59 | // let's see if it is a simple url? |
53 | $url = $request->query->get('url', ''); | 60 | $url = $request->query->get('url', ''); |
54 | 61 | ||
55 | if (empty($url)) { | 62 | if (empty($url)) { |
56 | throw $this->createAccessDeniedException('URL is empty?, logged user id: '.$this->getUser()->getId()); | 63 | throw $this->createAccessDeniedException('URL is empty?, logged user id: ' . $this->getUser()->getId()); |
57 | } | 64 | } |
58 | 65 | ||
59 | $res = $this->getDoctrine() | 66 | $res = $this->getDoctrine() |
60 | ->getRepository('WallabagCoreBundle:Entry') | 67 | ->getRepository('WallabagCoreBundle:Entry') |
61 | ->findByUrlAndUserId($url, $this->getUser()->getId()); | 68 | ->findByUrlAndUserId($url, $this->getUser()->getId()); |
62 | 69 | ||
63 | $exists = false === $res ? false : true; | 70 | $exists = $this->returnExistInformation($res, $returnId); |
64 | |||
65 | $json = $this->get('serializer')->serialize(['exists' => $exists], 'json'); | ||
66 | 71 | ||
67 | return (new JsonResponse())->setJson($json); | 72 | return $this->sendResponse(['exists' => $exists]); |
68 | } | 73 | } |
69 | 74 | ||
70 | /** | 75 | /** |
@@ -80,6 +85,7 @@ class EntryRestController extends WallabagRestController | |||
80 | * {"name"="perPage", "dataType"="integer", "required"=false, "format"="default'30'", "description"="results per page."}, | 85 | * {"name"="perPage", "dataType"="integer", "required"=false, "format"="default'30'", "description"="results per page."}, |
81 | * {"name"="tags", "dataType"="string", "required"=false, "format"="api,rest", "description"="a list of tags url encoded. Will returns entries that matches ALL tags."}, | 86 | * {"name"="tags", "dataType"="string", "required"=false, "format"="api,rest", "description"="a list of tags url encoded. Will returns entries that matches ALL tags."}, |
82 | * {"name"="since", "dataType"="integer", "required"=false, "format"="default '0'", "description"="The timestamp since when you want entries updated."}, | 87 | * {"name"="since", "dataType"="integer", "required"=false, "format"="default '0'", "description"="The timestamp since when you want entries updated."}, |
88 | * {"name"="public", "dataType"="integer", "required"=false, "format"="1 or 0, all entries by default", "description"="filter by entries with a public link"}, | ||
83 | * } | 89 | * } |
84 | * ) | 90 | * ) |
85 | * | 91 | * |
@@ -91,6 +97,7 @@ class EntryRestController extends WallabagRestController | |||
91 | 97 | ||
92 | $isArchived = (null === $request->query->get('archive')) ? null : (bool) $request->query->get('archive'); | 98 | $isArchived = (null === $request->query->get('archive')) ? null : (bool) $request->query->get('archive'); |
93 | $isStarred = (null === $request->query->get('starred')) ? null : (bool) $request->query->get('starred'); | 99 | $isStarred = (null === $request->query->get('starred')) ? null : (bool) $request->query->get('starred'); |
100 | $isPublic = (null === $request->query->get('public')) ? null : (bool) $request->query->get('public'); | ||
94 | $sort = $request->query->get('sort', 'created'); | 101 | $sort = $request->query->get('sort', 'created'); |
95 | $order = $request->query->get('order', 'desc'); | 102 | $order = $request->query->get('order', 'desc'); |
96 | $page = (int) $request->query->get('page', 1); | 103 | $page = (int) $request->query->get('page', 1); |
@@ -99,9 +106,16 @@ class EntryRestController extends WallabagRestController | |||
99 | $since = $request->query->get('since', 0); | 106 | $since = $request->query->get('since', 0); |
100 | 107 | ||
101 | /** @var \Pagerfanta\Pagerfanta $pager */ | 108 | /** @var \Pagerfanta\Pagerfanta $pager */ |
102 | $pager = $this->getDoctrine() | 109 | $pager = $this->get('wallabag_core.entry_repository')->findEntries( |
103 | ->getRepository('WallabagCoreBundle:Entry') | 110 | $this->getUser()->getId(), |
104 | ->findEntries($this->getUser()->getId(), $isArchived, $isStarred, $sort, $order, $since, $tags); | 111 | $isArchived, |
112 | $isStarred, | ||
113 | $isPublic, | ||
114 | $sort, | ||
115 | $order, | ||
116 | $since, | ||
117 | $tags | ||
118 | ); | ||
105 | 119 | ||
106 | $pager->setMaxPerPage($perPage); | 120 | $pager->setMaxPerPage($perPage); |
107 | $pager->setCurrentPage($page); | 121 | $pager->setCurrentPage($page); |
@@ -114,6 +128,7 @@ class EntryRestController extends WallabagRestController | |||
114 | [ | 128 | [ |
115 | 'archive' => $isArchived, | 129 | 'archive' => $isArchived, |
116 | 'starred' => $isStarred, | 130 | 'starred' => $isStarred, |
131 | 'public' => $isPublic, | ||
117 | 'sort' => $sort, | 132 | 'sort' => $sort, |
118 | 'order' => $order, | 133 | 'order' => $order, |
119 | 'page' => $page, | 134 | 'page' => $page, |
@@ -125,9 +140,7 @@ class EntryRestController extends WallabagRestController | |||
125 | ) | 140 | ) |
126 | ); | 141 | ); |
127 | 142 | ||
128 | $json = $this->get('serializer')->serialize($paginatedCollection, 'json'); | 143 | return $this->sendResponse($paginatedCollection); |
129 | |||
130 | return (new JsonResponse())->setJson($json); | ||
131 | } | 144 | } |
132 | 145 | ||
133 | /** | 146 | /** |
@@ -146,9 +159,7 @@ class EntryRestController extends WallabagRestController | |||
146 | $this->validateAuthentication(); | 159 | $this->validateAuthentication(); |
147 | $this->validateUserAccess($entry->getUser()->getId()); | 160 | $this->validateUserAccess($entry->getUser()->getId()); |
148 | 161 | ||
149 | $json = $this->get('serializer')->serialize($entry, 'json'); | 162 | return $this->sendResponse($entry); |
150 | |||
151 | return (new JsonResponse())->setJson($json); | ||
152 | } | 163 | } |
153 | 164 | ||
154 | /** | 165 | /** |
@@ -170,19 +181,134 @@ class EntryRestController extends WallabagRestController | |||
170 | return $this->get('wallabag_core.helper.entries_export') | 181 | return $this->get('wallabag_core.helper.entries_export') |
171 | ->setEntries($entry) | 182 | ->setEntries($entry) |
172 | ->updateTitle('entry') | 183 | ->updateTitle('entry') |
184 | ->updateAuthor('entry') | ||
173 | ->exportAs($request->attributes->get('_format')); | 185 | ->exportAs($request->attributes->get('_format')); |
174 | } | 186 | } |
175 | 187 | ||
176 | /** | 188 | /** |
189 | * Handles an entries list and delete URL. | ||
190 | * | ||
191 | * @ApiDoc( | ||
192 | * parameters={ | ||
193 | * {"name"="urls", "dataType"="string", "required"=true, "format"="A JSON array of urls [{'url': 'http://...'}, {'url': 'http://...'}]", "description"="Urls (as an array) to delete."} | ||
194 | * } | ||
195 | * ) | ||
196 | * | ||
197 | * @return JsonResponse | ||
198 | */ | ||
199 | public function deleteEntriesListAction(Request $request) | ||
200 | { | ||
201 | $this->validateAuthentication(); | ||
202 | |||
203 | $urls = json_decode($request->query->get('urls', [])); | ||
204 | |||
205 | if (empty($urls)) { | ||
206 | return $this->sendResponse([]); | ||
207 | } | ||
208 | |||
209 | $results = []; | ||
210 | |||
211 | // handle multiple urls | ||
212 | foreach ($urls as $key => $url) { | ||
213 | $entry = $this->get('wallabag_core.entry_repository')->findByUrlAndUserId( | ||
214 | $url, | ||
215 | $this->getUser()->getId() | ||
216 | ); | ||
217 | |||
218 | $results[$key]['url'] = $url; | ||
219 | |||
220 | if (false !== $entry) { | ||
221 | $em = $this->getDoctrine()->getManager(); | ||
222 | $em->remove($entry); | ||
223 | $em->flush(); | ||
224 | |||
225 | // entry deleted, dispatch event about it! | ||
226 | $this->get('event_dispatcher')->dispatch(EntryDeletedEvent::NAME, new EntryDeletedEvent($entry)); | ||
227 | } | ||
228 | |||
229 | $results[$key]['entry'] = $entry instanceof Entry ? true : false; | ||
230 | } | ||
231 | |||
232 | return $this->sendResponse($results); | ||
233 | } | ||
234 | |||
235 | /** | ||
236 | * Handles an entries list and create URL. | ||
237 | * | ||
238 | * @ApiDoc( | ||
239 | * parameters={ | ||
240 | * {"name"="urls", "dataType"="string", "required"=true, "format"="A JSON array of urls [{'url': 'http://...'}, {'url': 'http://...'}]", "description"="Urls (as an array) to create."} | ||
241 | * } | ||
242 | * ) | ||
243 | * | ||
244 | * @throws HttpException When limit is reached | ||
245 | * | ||
246 | * @return JsonResponse | ||
247 | */ | ||
248 | public function postEntriesListAction(Request $request) | ||
249 | { | ||
250 | $this->validateAuthentication(); | ||
251 | |||
252 | $urls = json_decode($request->query->get('urls', [])); | ||
253 | |||
254 | $limit = $this->container->getParameter('wallabag_core.api_limit_mass_actions'); | ||
255 | |||
256 | if (count($urls) > $limit) { | ||
257 | throw new HttpException(400, 'API limit reached'); | ||
258 | } | ||
259 | |||
260 | $results = []; | ||
261 | if (empty($urls)) { | ||
262 | return $this->sendResponse($results); | ||
263 | } | ||
264 | |||
265 | // handle multiple urls | ||
266 | foreach ($urls as $key => $url) { | ||
267 | $entry = $this->get('wallabag_core.entry_repository')->findByUrlAndUserId( | ||
268 | $url, | ||
269 | $this->getUser()->getId() | ||
270 | ); | ||
271 | |||
272 | $results[$key]['url'] = $url; | ||
273 | |||
274 | if (false === $entry) { | ||
275 | $entry = new Entry($this->getUser()); | ||
276 | |||
277 | $this->get('wallabag_core.content_proxy')->updateEntry($entry, $url); | ||
278 | } | ||
279 | |||
280 | $em = $this->getDoctrine()->getManager(); | ||
281 | $em->persist($entry); | ||
282 | $em->flush(); | ||
283 | |||
284 | $results[$key]['entry'] = $entry instanceof Entry ? $entry->getId() : false; | ||
285 | |||
286 | // entry saved, dispatch event about it! | ||
287 | $this->get('event_dispatcher')->dispatch(EntrySavedEvent::NAME, new EntrySavedEvent($entry)); | ||
288 | } | ||
289 | |||
290 | return $this->sendResponse($results); | ||
291 | } | ||
292 | |||
293 | /** | ||
177 | * Create an entry. | 294 | * Create an entry. |
178 | * | 295 | * |
296 | * 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**. | ||
297 | * Otherwise, content will be fetched as normal from the url and values will be overwritten. | ||
298 | * | ||
179 | * @ApiDoc( | 299 | * @ApiDoc( |
180 | * parameters={ | 300 | * parameters={ |
181 | * {"name"="url", "dataType"="string", "required"=true, "format"="http://www.test.com/article.html", "description"="Url for the entry."}, | 301 | * {"name"="url", "dataType"="string", "required"=true, "format"="http://www.test.com/article.html", "description"="Url for the entry."}, |
182 | * {"name"="title", "dataType"="string", "required"=false, "description"="Optional, we'll get the title from the page."}, | 302 | * {"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."}, | 303 | * {"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"}, | 304 | * {"name"="archive", "dataType"="integer", "required"=false, "format"="1 or 0", "description"="entry already archived"}, |
305 | * {"name"="starred", "dataType"="integer", "required"=false, "format"="1 or 0", "description"="entry already starred"}, | ||
306 | * {"name"="content", "dataType"="string", "required"=false, "description"="Content of the entry"}, | ||
307 | * {"name"="language", "dataType"="string", "required"=false, "description"="Language of the entry"}, | ||
308 | * {"name"="preview_picture", "dataType"="string", "required"=false, "description"="Preview picture of the entry"}, | ||
309 | * {"name"="published_at", "dataType"="datetime|integer", "format"="YYYY-MM-DDTHH:II:SS+TZ or a timestamp", "required"=false, "description"="Published date of the entry"}, | ||
310 | * {"name"="authors", "dataType"="string", "format"="Name Firstname,author2,author3", "required"=false, "description"="Authors of the entry"}, | ||
311 | * {"name"="public", "dataType"="integer", "required"=false, "format"="1 or 0", "description"="will generate a public link for the entry"}, | ||
186 | * } | 312 | * } |
187 | * ) | 313 | * ) |
188 | * | 314 | * |
@@ -193,43 +319,61 @@ class EntryRestController extends WallabagRestController | |||
193 | $this->validateAuthentication(); | 319 | $this->validateAuthentication(); |
194 | 320 | ||
195 | $url = $request->request->get('url'); | 321 | $url = $request->request->get('url'); |
196 | $title = $request->request->get('title'); | ||
197 | $isArchived = $request->request->get('archive'); | ||
198 | $isStarred = $request->request->get('starred'); | ||
199 | 322 | ||
200 | $entry = $this->get('wallabag_core.entry_repository')->findByUrlAndUserId($url, $this->getUser()->getId()); | 323 | $entry = $this->get('wallabag_core.entry_repository')->findByUrlAndUserId( |
324 | $url, | ||
325 | $this->getUser()->getId() | ||
326 | ); | ||
201 | 327 | ||
202 | if (false === $entry) { | 328 | if (false === $entry) { |
203 | $entry = new Entry($this->getUser()); | 329 | $entry = new Entry($this->getUser()); |
204 | try { | 330 | $entry->setUrl($url); |
205 | $entry = $this->get('wallabag_core.content_proxy')->updateEntry( | ||
206 | $entry, | ||
207 | $url | ||
208 | ); | ||
209 | } catch (\Exception $e) { | ||
210 | $this->get('logger')->error('Error while saving an entry', [ | ||
211 | 'exception' => $e, | ||
212 | 'entry' => $entry, | ||
213 | ]); | ||
214 | $entry->setUrl($url); | ||
215 | } | ||
216 | } | 331 | } |
217 | 332 | ||
218 | if (!is_null($title)) { | 333 | $data = $this->retrieveValueFromRequest($request); |
219 | $entry->setTitle($title); | 334 | |
335 | try { | ||
336 | $this->get('wallabag_core.content_proxy')->updateEntry( | ||
337 | $entry, | ||
338 | $entry->getUrl(), | ||
339 | [ | ||
340 | 'title' => !empty($data['title']) ? $data['title'] : $entry->getTitle(), | ||
341 | 'html' => !empty($data['content']) ? $data['content'] : $entry->getContent(), | ||
342 | 'url' => $entry->getUrl(), | ||
343 | 'language' => !empty($data['language']) ? $data['language'] : $entry->getLanguage(), | ||
344 | 'date' => !empty($data['publishedAt']) ? $data['publishedAt'] : $entry->getPublishedAt(), | ||
345 | // faking the open graph preview picture | ||
346 | 'open_graph' => [ | ||
347 | 'og_image' => !empty($data['picture']) ? $data['picture'] : $entry->getPreviewPicture(), | ||
348 | ], | ||
349 | 'authors' => is_string($data['authors']) ? explode(',', $data['authors']) : $entry->getPublishedBy(), | ||
350 | ] | ||
351 | ); | ||
352 | } catch (\Exception $e) { | ||
353 | $this->get('logger')->error('Error while saving an entry', [ | ||
354 | 'exception' => $e, | ||
355 | 'entry' => $entry, | ||
356 | ]); | ||
220 | } | 357 | } |
221 | 358 | ||
222 | $tags = $request->request->get('tags', ''); | 359 | if (null !== $data['isArchived']) { |
223 | if (!empty($tags)) { | 360 | $entry->setArchived((bool) $data['isArchived']); |
224 | $this->get('wallabag_core.content_proxy')->assignTagsToEntry($entry, $tags); | 361 | } |
362 | |||
363 | if (null !== $data['isStarred']) { | ||
364 | $entry->setStarred((bool) $data['isStarred']); | ||
225 | } | 365 | } |
226 | 366 | ||
227 | if (!is_null($isStarred)) { | 367 | if (!empty($data['tags'])) { |
228 | $entry->setStarred((bool) $isStarred); | 368 | $this->get('wallabag_core.tags_assigner')->assignTagsToEntry($entry, $data['tags']); |
229 | } | 369 | } |
230 | 370 | ||
231 | if (!is_null($isArchived)) { | 371 | if (null !== $data['isPublic']) { |
232 | $entry->setArchived((bool) $isArchived); | 372 | if (true === (bool) $data['isPublic'] && null === $entry->getUid()) { |
373 | $entry->generateUid(); | ||
374 | } elseif (false === (bool) $data['isPublic']) { | ||
375 | $entry->cleanUid(); | ||
376 | } | ||
233 | } | 377 | } |
234 | 378 | ||
235 | $em = $this->getDoctrine()->getManager(); | 379 | $em = $this->getDoctrine()->getManager(); |
@@ -239,9 +383,7 @@ class EntryRestController extends WallabagRestController | |||
239 | // entry saved, dispatch event about it! | 383 | // entry saved, dispatch event about it! |
240 | $this->get('event_dispatcher')->dispatch(EntrySavedEvent::NAME, new EntrySavedEvent($entry)); | 384 | $this->get('event_dispatcher')->dispatch(EntrySavedEvent::NAME, new EntrySavedEvent($entry)); |
241 | 385 | ||
242 | $json = $this->get('serializer')->serialize($entry, 'json'); | 386 | return $this->sendResponse($entry); |
243 | |||
244 | return (new JsonResponse())->setJson($json); | ||
245 | } | 387 | } |
246 | 388 | ||
247 | /** | 389 | /** |
@@ -256,6 +398,12 @@ class EntryRestController extends WallabagRestController | |||
256 | * {"name"="tags", "dataType"="string", "required"=false, "format"="tag1,tag2,tag3", "description"="a comma-separated list of tags."}, | 398 | * {"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."}, | 399 | * {"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."}, | 400 | * {"name"="starred", "dataType"="integer", "required"=false, "format"="1 or 0", "description"="starred the entry."}, |
401 | * {"name"="content", "dataType"="string", "required"=false, "description"="Content of the entry"}, | ||
402 | * {"name"="language", "dataType"="string", "required"=false, "description"="Language of the entry"}, | ||
403 | * {"name"="preview_picture", "dataType"="string", "required"=false, "description"="Preview picture of the entry"}, | ||
404 | * {"name"="published_at", "dataType"="datetime|integer", "format"="YYYY-MM-DDTHH:II:SS+TZ or a timestamp", "required"=false, "description"="Published date of the entry"}, | ||
405 | * {"name"="authors", "dataType"="string", "format"="Name Firstname,author2,author3", "required"=false, "description"="Authors of the entry"}, | ||
406 | * {"name"="public", "dataType"="integer", "required"=false, "format"="1 or 0", "description"="will generate a public link for the entry"}, | ||
259 | * } | 407 | * } |
260 | * ) | 408 | * ) |
261 | * | 409 | * |
@@ -266,33 +414,80 @@ class EntryRestController extends WallabagRestController | |||
266 | $this->validateAuthentication(); | 414 | $this->validateAuthentication(); |
267 | $this->validateUserAccess($entry->getUser()->getId()); | 415 | $this->validateUserAccess($entry->getUser()->getId()); |
268 | 416 | ||
269 | $title = $request->request->get('title'); | 417 | $contentProxy = $this->get('wallabag_core.content_proxy'); |
270 | $isArchived = $request->request->get('archive'); | ||
271 | $isStarred = $request->request->get('starred'); | ||
272 | 418 | ||
273 | if (!is_null($title)) { | 419 | $data = $this->retrieveValueFromRequest($request); |
274 | $entry->setTitle($title); | 420 | |
421 | // this is a special case where user want to manually update the entry content | ||
422 | // the ContentProxy will only cleanup the html | ||
423 | // and also we force to not re-fetch the content in case of error | ||
424 | if (!empty($data['content'])) { | ||
425 | try { | ||
426 | $contentProxy->updateEntry( | ||
427 | $entry, | ||
428 | $entry->getUrl(), | ||
429 | [ | ||
430 | 'html' => $data['content'], | ||
431 | ], | ||
432 | true | ||
433 | ); | ||
434 | } catch (\Exception $e) { | ||
435 | $this->get('logger')->error('Error while saving an entry', [ | ||
436 | 'exception' => $e, | ||
437 | 'entry' => $entry, | ||
438 | ]); | ||
439 | } | ||
275 | } | 440 | } |
276 | 441 | ||
277 | if (!is_null($isArchived)) { | 442 | if (!empty($data['title'])) { |
278 | $entry->setArchived((bool) $isArchived); | 443 | $entry->setTitle($data['title']); |
279 | } | 444 | } |
280 | 445 | ||
281 | if (!is_null($isStarred)) { | 446 | if (!empty($data['language'])) { |
282 | $entry->setStarred((bool) $isStarred); | 447 | $contentProxy->updateLanguage($entry, $data['language']); |
283 | } | 448 | } |
284 | 449 | ||
285 | $tags = $request->request->get('tags', ''); | 450 | if (!empty($data['authors']) && is_string($data['authors'])) { |
286 | if (!empty($tags)) { | 451 | $entry->setPublishedBy(explode(',', $data['authors'])); |
287 | $this->get('wallabag_core.content_proxy')->assignTagsToEntry($entry, $tags); | 452 | } |
453 | |||
454 | if (!empty($data['picture'])) { | ||
455 | $contentProxy->updatePreviewPicture($entry, $data['picture']); | ||
456 | } | ||
457 | |||
458 | if (!empty($data['publishedAt'])) { | ||
459 | $contentProxy->updatePublishedAt($entry, $data['publishedAt']); | ||
460 | } | ||
461 | |||
462 | if (null !== $data['isArchived']) { | ||
463 | $entry->setArchived((bool) $data['isArchived']); | ||
464 | } | ||
465 | |||
466 | if (null !== $data['isStarred']) { | ||
467 | $entry->setStarred((bool) $data['isStarred']); | ||
468 | } | ||
469 | |||
470 | if (!empty($data['tags'])) { | ||
471 | $entry->removeAllTags(); | ||
472 | $this->get('wallabag_core.tags_assigner')->assignTagsToEntry($entry, $data['tags']); | ||
473 | } | ||
474 | |||
475 | if (null !== $data['isPublic']) { | ||
476 | if (true === (bool) $data['isPublic'] && null === $entry->getUid()) { | ||
477 | $entry->generateUid(); | ||
478 | } elseif (false === (bool) $data['isPublic']) { | ||
479 | $entry->cleanUid(); | ||
480 | } | ||
288 | } | 481 | } |
289 | 482 | ||
290 | $em = $this->getDoctrine()->getManager(); | 483 | $em = $this->getDoctrine()->getManager(); |
484 | $em->persist($entry); | ||
291 | $em->flush(); | 485 | $em->flush(); |
292 | 486 | ||
293 | $json = $this->get('serializer')->serialize($entry, 'json'); | 487 | // entry saved, dispatch event about it! |
488 | $this->get('event_dispatcher')->dispatch(EntrySavedEvent::NAME, new EntrySavedEvent($entry)); | ||
294 | 489 | ||
295 | return (new JsonResponse())->setJson($json); | 490 | return $this->sendResponse($entry); |
296 | } | 491 | } |
297 | 492 | ||
298 | /** | 493 | /** |
@@ -313,7 +508,7 @@ class EntryRestController extends WallabagRestController | |||
313 | $this->validateUserAccess($entry->getUser()->getId()); | 508 | $this->validateUserAccess($entry->getUser()->getId()); |
314 | 509 | ||
315 | try { | 510 | try { |
316 | $entry = $this->get('wallabag_core.content_proxy')->updateEntry($entry, $entry->getUrl()); | 511 | $this->get('wallabag_core.content_proxy')->updateEntry($entry, $entry->getUrl()); |
317 | } catch (\Exception $e) { | 512 | } catch (\Exception $e) { |
318 | $this->get('logger')->error('Error while saving an entry', [ | 513 | $this->get('logger')->error('Error while saving an entry', [ |
319 | 'exception' => $e, | 514 | 'exception' => $e, |
@@ -335,9 +530,7 @@ class EntryRestController extends WallabagRestController | |||
335 | // entry saved, dispatch event about it! | 530 | // entry saved, dispatch event about it! |
336 | $this->get('event_dispatcher')->dispatch(EntrySavedEvent::NAME, new EntrySavedEvent($entry)); | 531 | $this->get('event_dispatcher')->dispatch(EntrySavedEvent::NAME, new EntrySavedEvent($entry)); |
337 | 532 | ||
338 | $json = $this->get('serializer')->serialize($entry, 'json'); | 533 | return $this->sendResponse($entry); |
339 | |||
340 | return (new JsonResponse())->setJson($json); | ||
341 | } | 534 | } |
342 | 535 | ||
343 | /** | 536 | /** |
@@ -363,9 +556,7 @@ class EntryRestController extends WallabagRestController | |||
363 | // entry deleted, dispatch event about it! | 556 | // entry deleted, dispatch event about it! |
364 | $this->get('event_dispatcher')->dispatch(EntryDeletedEvent::NAME, new EntryDeletedEvent($entry)); | 557 | $this->get('event_dispatcher')->dispatch(EntryDeletedEvent::NAME, new EntryDeletedEvent($entry)); |
365 | 558 | ||
366 | $json = $this->get('serializer')->serialize($entry, 'json'); | 559 | return $this->sendResponse($entry); |
367 | |||
368 | return (new JsonResponse())->setJson($json); | ||
369 | } | 560 | } |
370 | 561 | ||
371 | /** | 562 | /** |
@@ -384,9 +575,7 @@ class EntryRestController extends WallabagRestController | |||
384 | $this->validateAuthentication(); | 575 | $this->validateAuthentication(); |
385 | $this->validateUserAccess($entry->getUser()->getId()); | 576 | $this->validateUserAccess($entry->getUser()->getId()); |
386 | 577 | ||
387 | $json = $this->get('serializer')->serialize($entry->getTags(), 'json'); | 578 | return $this->sendResponse($entry->getTags()); |
388 | |||
389 | return (new JsonResponse())->setJson($json); | ||
390 | } | 579 | } |
391 | 580 | ||
392 | /** | 581 | /** |
@@ -410,16 +599,14 @@ class EntryRestController extends WallabagRestController | |||
410 | 599 | ||
411 | $tags = $request->request->get('tags', ''); | 600 | $tags = $request->request->get('tags', ''); |
412 | if (!empty($tags)) { | 601 | if (!empty($tags)) { |
413 | $this->get('wallabag_core.content_proxy')->assignTagsToEntry($entry, $tags); | 602 | $this->get('wallabag_core.tags_assigner')->assignTagsToEntry($entry, $tags); |
414 | } | 603 | } |
415 | 604 | ||
416 | $em = $this->getDoctrine()->getManager(); | 605 | $em = $this->getDoctrine()->getManager(); |
417 | $em->persist($entry); | 606 | $em->persist($entry); |
418 | $em->flush(); | 607 | $em->flush(); |
419 | 608 | ||
420 | $json = $this->get('serializer')->serialize($entry, 'json'); | 609 | return $this->sendResponse($entry); |
421 | |||
422 | return (new JsonResponse())->setJson($json); | ||
423 | } | 610 | } |
424 | 611 | ||
425 | /** | 612 | /** |
@@ -444,8 +631,170 @@ class EntryRestController extends WallabagRestController | |||
444 | $em->persist($entry); | 631 | $em->persist($entry); |
445 | $em->flush(); | 632 | $em->flush(); |
446 | 633 | ||
447 | $json = $this->get('serializer')->serialize($entry, 'json'); | 634 | return $this->sendResponse($entry); |
635 | } | ||
636 | |||
637 | /** | ||
638 | * Handles an entries list delete tags from them. | ||
639 | * | ||
640 | * @ApiDoc( | ||
641 | * parameters={ | ||
642 | * {"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."} | ||
643 | * } | ||
644 | * ) | ||
645 | * | ||
646 | * @return JsonResponse | ||
647 | */ | ||
648 | public function deleteEntriesTagsListAction(Request $request) | ||
649 | { | ||
650 | $this->validateAuthentication(); | ||
651 | |||
652 | $list = json_decode($request->query->get('list', [])); | ||
653 | |||
654 | if (empty($list)) { | ||
655 | return $this->sendResponse([]); | ||
656 | } | ||
657 | |||
658 | // handle multiple urls | ||
659 | $results = []; | ||
660 | |||
661 | foreach ($list as $key => $element) { | ||
662 | $entry = $this->get('wallabag_core.entry_repository')->findByUrlAndUserId( | ||
663 | $element->url, | ||
664 | $this->getUser()->getId() | ||
665 | ); | ||
666 | |||
667 | $results[$key]['url'] = $element->url; | ||
668 | $results[$key]['entry'] = $entry instanceof Entry ? $entry->getId() : false; | ||
669 | |||
670 | $tags = $element->tags; | ||
671 | |||
672 | if (false !== $entry && !(empty($tags))) { | ||
673 | $tags = explode(',', $tags); | ||
674 | foreach ($tags as $label) { | ||
675 | $label = trim($label); | ||
676 | |||
677 | $tag = $this->getDoctrine() | ||
678 | ->getRepository('WallabagCoreBundle:Tag') | ||
679 | ->findOneByLabel($label); | ||
680 | |||
681 | if (false !== $tag) { | ||
682 | $entry->removeTag($tag); | ||
683 | } | ||
684 | } | ||
685 | |||
686 | $em = $this->getDoctrine()->getManager(); | ||
687 | $em->persist($entry); | ||
688 | $em->flush(); | ||
689 | } | ||
690 | } | ||
691 | |||
692 | return $this->sendResponse($results); | ||
693 | } | ||
694 | |||
695 | /** | ||
696 | * Handles an entries list and add tags to them. | ||
697 | * | ||
698 | * @ApiDoc( | ||
699 | * parameters={ | ||
700 | * {"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."} | ||
701 | * } | ||
702 | * ) | ||
703 | * | ||
704 | * @return JsonResponse | ||
705 | */ | ||
706 | public function postEntriesTagsListAction(Request $request) | ||
707 | { | ||
708 | $this->validateAuthentication(); | ||
709 | |||
710 | $list = json_decode($request->query->get('list', [])); | ||
711 | |||
712 | if (empty($list)) { | ||
713 | return $this->sendResponse([]); | ||
714 | } | ||
715 | |||
716 | $results = []; | ||
717 | |||
718 | // handle multiple urls | ||
719 | foreach ($list as $key => $element) { | ||
720 | $entry = $this->get('wallabag_core.entry_repository')->findByUrlAndUserId( | ||
721 | $element->url, | ||
722 | $this->getUser()->getId() | ||
723 | ); | ||
724 | |||
725 | $results[$key]['url'] = $element->url; | ||
726 | $results[$key]['entry'] = $entry instanceof Entry ? $entry->getId() : false; | ||
727 | |||
728 | $tags = $element->tags; | ||
729 | |||
730 | if (false !== $entry && !(empty($tags))) { | ||
731 | $this->get('wallabag_core.tags_assigner')->assignTagsToEntry($entry, $tags); | ||
732 | |||
733 | $em = $this->getDoctrine()->getManager(); | ||
734 | $em->persist($entry); | ||
735 | $em->flush(); | ||
736 | } | ||
737 | } | ||
738 | |||
739 | return $this->sendResponse($results); | ||
740 | } | ||
741 | |||
742 | /** | ||
743 | * Shortcut to send data serialized in json. | ||
744 | * | ||
745 | * @param mixed $data | ||
746 | * | ||
747 | * @return JsonResponse | ||
748 | */ | ||
749 | private function sendResponse($data) | ||
750 | { | ||
751 | // https://github.com/schmittjoh/JMSSerializerBundle/issues/293 | ||
752 | $context = new SerializationContext(); | ||
753 | $context->setSerializeNull(true); | ||
754 | |||
755 | $json = $this->get('serializer')->serialize($data, 'json', $context); | ||
448 | 756 | ||
449 | return (new JsonResponse())->setJson($json); | 757 | return (new JsonResponse())->setJson($json); |
450 | } | 758 | } |
759 | |||
760 | /** | ||
761 | * Retrieve value from the request. | ||
762 | * Used for POST & PATCH on a an entry. | ||
763 | * | ||
764 | * @param Request $request | ||
765 | * | ||
766 | * @return array | ||
767 | */ | ||
768 | private function retrieveValueFromRequest(Request $request) | ||
769 | { | ||
770 | return [ | ||
771 | 'title' => $request->request->get('title'), | ||
772 | 'tags' => $request->request->get('tags', []), | ||
773 | 'isArchived' => $request->request->get('archive'), | ||
774 | 'isStarred' => $request->request->get('starred'), | ||
775 | 'isPublic' => $request->request->get('public'), | ||
776 | 'content' => $request->request->get('content'), | ||
777 | 'language' => $request->request->get('language'), | ||
778 | 'picture' => $request->request->get('preview_picture'), | ||
779 | 'publishedAt' => $request->request->get('published_at'), | ||
780 | 'authors' => $request->request->get('authors', ''), | ||
781 | ]; | ||
782 | } | ||
783 | |||
784 | /** | ||
785 | * Return information about the entry if it exist and depending on the id or not. | ||
786 | * | ||
787 | * @param Entry|null $entry | ||
788 | * @param bool $returnId | ||
789 | * | ||
790 | * @return bool|int | ||
791 | */ | ||
792 | private function returnExistInformation($entry, $returnId) | ||
793 | { | ||
794 | if ($returnId) { | ||
795 | return $entry instanceof Entry ? $entry->getId() : null; | ||
796 | } | ||
797 | |||
798 | return $entry instanceof Entry; | ||
799 | } | ||
451 | } | 800 | } |
diff --git a/src/Wallabag/ApiBundle/Controller/TagRestController.php b/src/Wallabag/ApiBundle/Controller/TagRestController.php index bc6d4e64..efa4e8cf 100644 --- a/src/Wallabag/ApiBundle/Controller/TagRestController.php +++ b/src/Wallabag/ApiBundle/Controller/TagRestController.php | |||
@@ -3,8 +3,8 @@ | |||
3 | namespace Wallabag\ApiBundle\Controller; | 3 | namespace Wallabag\ApiBundle\Controller; |
4 | 4 | ||
5 | use Nelmio\ApiDocBundle\Annotation\ApiDoc; | 5 | use Nelmio\ApiDocBundle\Annotation\ApiDoc; |
6 | use Symfony\Component\HttpFoundation\Request; | ||
7 | use Symfony\Component\HttpFoundation\JsonResponse; | 6 | use Symfony\Component\HttpFoundation\JsonResponse; |
7 | use Symfony\Component\HttpFoundation\Request; | ||
8 | use Wallabag\CoreBundle\Entity\Entry; | 8 | use Wallabag\CoreBundle\Entity\Entry; |
9 | use Wallabag\CoreBundle\Entity\Tag; | 9 | use Wallabag\CoreBundle\Entity\Tag; |
10 | 10 | ||
@@ -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..6f47cff0 --- /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\JsonResponse; | ||
10 | use Symfony\Component\HttpFoundation\Request; | ||
11 | use Wallabag\ApiBundle\Entity\Client; | ||
12 | use Wallabag\UserBundle\Entity\User; | ||
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/Controller/WallabagRestController.php b/src/Wallabag/ApiBundle/Controller/WallabagRestController.php index b1e08ca4..71da2a64 100644 --- a/src/Wallabag/ApiBundle/Controller/WallabagRestController.php +++ b/src/Wallabag/ApiBundle/Controller/WallabagRestController.php | |||
@@ -40,8 +40,8 @@ class WallabagRestController extends FOSRestController | |||
40 | protected function validateUserAccess($requestUserId) | 40 | protected function validateUserAccess($requestUserId) |
41 | { | 41 | { |
42 | $user = $this->get('security.token_storage')->getToken()->getUser(); | 42 | $user = $this->get('security.token_storage')->getToken()->getUser(); |
43 | if ($requestUserId != $user->getId()) { | 43 | if ($requestUserId !== $user->getId()) { |
44 | throw $this->createAccessDeniedException('Access forbidden. Entry user id: '.$requestUserId.', logged user id: '.$user->getId()); | 44 | throw $this->createAccessDeniedException('Access forbidden. Entry user id: ' . $requestUserId . ', logged user id: ' . $user->getId()); |
45 | } | 45 | } |
46 | } | 46 | } |
47 | } | 47 | } |
diff --git a/src/Wallabag/ApiBundle/Entity/Client.php b/src/Wallabag/ApiBundle/Entity/Client.php index 9ed9f980..e6f98f98 100644 --- a/src/Wallabag/ApiBundle/Entity/Client.php +++ b/src/Wallabag/ApiBundle/Entity/Client.php | |||
@@ -4,6 +4,9 @@ namespace Wallabag\ApiBundle\Entity; | |||
4 | 4 | ||
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 JMS\Serializer\Annotation\Groups; | ||
8 | use JMS\Serializer\Annotation\SerializedName; | ||
9 | use JMS\Serializer\Annotation\VirtualProperty; | ||
7 | use Wallabag\UserBundle\Entity\User; | 10 | use Wallabag\UserBundle\Entity\User; |
8 | 11 | ||
9 | /** | 12 | /** |
@@ -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/Form/Type/ClientType.php b/src/Wallabag/ApiBundle/Form/Type/ClientType.php index eaea4feb..fc22538f 100644 --- a/src/Wallabag/ApiBundle/Form/Type/ClientType.php +++ b/src/Wallabag/ApiBundle/Form/Type/ClientType.php | |||
@@ -5,8 +5,8 @@ namespace Wallabag\ApiBundle\Form\Type; | |||
5 | use Symfony\Component\Form\AbstractType; | 5 | use Symfony\Component\Form\AbstractType; |
6 | use Symfony\Component\Form\CallbackTransformer; | 6 | use Symfony\Component\Form\CallbackTransformer; |
7 | use Symfony\Component\Form\Extension\Core\Type\SubmitType; | 7 | use Symfony\Component\Form\Extension\Core\Type\SubmitType; |
8 | use Symfony\Component\Form\Extension\Core\Type\UrlType; | ||
9 | use Symfony\Component\Form\Extension\Core\Type\TextType; | 8 | use Symfony\Component\Form\Extension\Core\Type\TextType; |
9 | use Symfony\Component\Form\Extension\Core\Type\UrlType; | ||
10 | use Symfony\Component\Form\FormBuilderInterface; | 10 | use Symfony\Component\Form\FormBuilderInterface; |
11 | use Symfony\Component\OptionsResolver\OptionsResolver; | 11 | use Symfony\Component\OptionsResolver\OptionsResolver; |
12 | 12 | ||
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_ | ||